home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / DTS Sample Code / System 7.0 Samples / ProcDoggie 1.0a6⁄THINK P / UProcessGuts.p < prev    next >
Encoding:
Text File  |  1991-02-21  |  94.0 KB  |  2,556 lines  |  [TEXT/PJMM]

  1. unit UProcessGuts;
  2.  
  3. {-------------------------------------------------------------------------------}
  4. {#}
  5. {#    Apple Macintosh Developer Technical Support}
  6. {#}
  7. {#    Interfaces for the guts of the ProcDoggie application}
  8. {#}
  9. {#    Program:    ProcDoggie}
  10. {#    File:        UProcessGuts.p - Pascal Implementation}
  11. {#}
  12. {#    by:        Forrest Tanaka}
  13. {#}
  14. {#    Copyright © 1988-1991 Apple Computer, Inc.}
  15. {#    All rights reserved.}
  16. {#}
  17. {--------------------------------------------------------------------------------}
  18. {#}
  19. {#    Process Manager-related code that’s specific to ProcDoggie is contained in}
  20. {#    this unit.  Mainly, this code handles the user interface aspects of this}
  21. {#    program that relate to the Process Manager, such as the Process List window,}
  22. {#    the Process Information windows, and most of the menus.}
  23. {#}
  24. {#    The Process List window displays a list of all active processes.  It allows}
  25. {#    the user to click on one or more of the process names and then use menu}
  26. {#    commands to operate on those selected processes.}
  27. {#}
  28. {#    Process Information windows display information about the selected active}
  29. {#    processes.  The information includes the process’s name, type, creator, SIZE}
  30. {#    resource flags, partition size, and free memory availability.}
  31. {#}
  32. {#    When the user chooses to launch an application, routines in this unit are}
  33. {#    called to ask the user what process to launch and optionally what documents}
  34. {#    to open or print, depending on the current launch mode.}
  35. {#}
  36. {#    This unit also maintains the current launch mode.  The launch mode indicates}
  37. {#    whether the user wants to simply launch an application, launch an application}
  38. {#    along with documents to open, and launch an application with documents to}
  39. {#    print.  The launch mode is global for this application, and affects the way}
  40. {#    the menu commands that launch applications work.}
  41. {#}
  42. {-------------------------------------------------------------------------------}
  43. {[j=20/57/1$] Pasmat Options}
  44.  
  45. {-------------------------------------------------------------------------------}
  46. {#}
  47. {#     2/21/91 pvh - THINK Pascal conversion.}
  48. {#    Notes:}
  49. {#        Slammed contents of UMenuHandler.p in here as well.  THINK P doesn't handle cyclic references well.}
  50. {#}
  51. {-------------------------------------------------------------------------------}
  52.  
  53. interface
  54.  
  55.  
  56. (*******************************************************************************}
  57. {* Units}
  58. {*******************************************************************************)
  59.  
  60.     uses
  61.         (* Group 1 *)
  62.         Types, QuickDraw, 
  63.  
  64.         (* Group 2 *)
  65.         AppleTalk, PPCToolBox, OSUtils, Files, Processes, EPPC, Notification, AppleEvents, Controls, Errors, Events, GestaltEqu, Memory, Menus, Resources, SegLoad, SysEqu, ToolUtils, 
  66.  
  67.         (* Group 3 *)
  68.         Aliases, Lists, Script, Windows, 
  69.  
  70.         (* Group 4 *)
  71.         Dialogs, Palettes, StandardFile, 
  72.  
  73.         (* Group 5 *)
  74.         Packages, TextEdit, 
  75.  
  76.         (* Application *)
  77.         UGlobals, UDialogUtils, UEmergMem, UProcessUtils,{ UMenuHandler,}
  78.         UProcessLDEF;
  79.  
  80.  
  81. (*******************************************************************************}
  82. {* SetLaunchMode - Set the launch mode}
  83. {*}
  84. {* This routine is called to set the launching mode to the mode specified by the}
  85. {* "newMode" parameter.  A launch mode of "kJustLaunch" simply launches an}
  86. {* application or desk accessory, "kOpenLaunch" launches an application with one}
  87. {* or more documents for the launched application to open, "kPrintLaunch"}
  88. {* launches an application with one or more documents for the launched}
  89. {* application to print.  The launch modes are declared in UProcessUtils.p.}
  90. {*******************************************************************************)
  91.  
  92.     procedure SetLaunchMode (newMode: LaunchModeCode);
  93.  
  94.  
  95. (*******************************************************************************}
  96. {* GetLaunchMode - Get the launch mode}
  97. {*}
  98. {* The current launch mode is returned.  For details about launching modes, see}
  99. {* the description of the SetLaunchMode routine.}
  100. {*******************************************************************************)
  101.  
  102.     function GetLaunchMode: LaunchModeCode;
  103.  
  104.  
  105. (*******************************************************************************}
  106. {* IsProcessListWindow - Is a WindowPtr a pointer to a process list window?}
  107. {*}
  108. {* When I want to find out whether a window that I have a pointer to is a process}
  109. {* list window or not, I call this routine.  It returns TRUE if aWindow is a}
  110. {* pointer to a process list window, FALSE if it isn’t.  If aWindow is NIL, then}
  111. {* IsProcessListWindow returns FALSE.}
  112. {*******************************************************************************)
  113.  
  114.     function IsProcessListWindow (aWindow: WindowPtr): Boolean;
  115.  
  116.  
  117. (*******************************************************************************}
  118. {* CreateProcessListWindow - Create a process list window}
  119. {*}
  120. {* This routine is called to create a new Process List window visible and}
  121. {* centered on the main screen.  A pointer to the window is returned.  If there}
  122. {* isn’t enough memory for the window, or if some other problem happened to make}
  123. {* it impossible to create the window, then CreateProcessListWindow puts up an}
  124. {* alert indicating this to the user, and then it returns NIL.}
  125. {*******************************************************************************)
  126.  
  127.     function CreateProcessListWindow: WindowPtr;
  128.  
  129.  
  130. (*******************************************************************************}
  131. {* IdleProcessListWindow - Keep the process list window up to date}
  132. {*}
  133. {* IdleProcessListWindow updates the process list window specified by}
  134. {* processListWindow so that it reflects the status of the processes that are}
  135. {* currently open.  It’s called once per main event loop iteration.}
  136. {*******************************************************************************)
  137.  
  138.     procedure IdleProcessListWindow (processListWindow: WindowPtr);
  139.  
  140.  
  141. (*******************************************************************************}
  142. {* DrawProcessListWindow - Draw the contents of the process list window}
  143. {*}
  144. {* Whenever an update event is received for the process list window, the routine}
  145. {* is called to draw into the window.  Since the process list covers the entire}
  146. {* window, the only thing to be done is to call the List Manager to draw the}
  147. {* list.  processListWindow is a pointer to the process list window.}
  148. {*******************************************************************************)
  149.  
  150.     procedure DrawProcessListWindow (processListWindow: WindowPtr);
  151.  
  152.  
  153. (*******************************************************************************}
  154. {* ClickProcessListWindow - Handle a mouse click in the process list window}
  155. {*}
  156. {* When a mouse click is detected in the content region of the process list}
  157. {* window specified by processListWindow, this routine is called to handle it.}
  158. {* It allows the user to select a process or several processes in the list.  If}
  159. {* the user double-clicks on a process, then that process and any other selected}
  160. {* processes are brought to the front.}
  161. {*}
  162. {* clickEvent is the mouse-down event that was in the process list window.}
  163. {*******************************************************************************)
  164.  
  165.     procedure ClickProcessListWindow (processListWindow: WindowPtr; clickEvent: EventRecord);
  166.  
  167.  
  168. (*******************************************************************************}
  169. {* ActivateProcessListWindow - Handle an activate/deactivate event}
  170. {*}
  171. {* Whenever an activate or deactivate event is received for the process list}
  172. {* window specified by processListWindow, this routine is called to handle the}
  173. {* event.  If the event was an activate event, then becomingActive is TRUE.  If}
  174. {* the event was a deactivate event, then becomingActive is FALSE.}
  175. {*******************************************************************************)
  176.  
  177.     procedure ActivateProcessListWindow (processListWindow: WindowPtr; becomingActive: Boolean);
  178.  
  179.  
  180. (*******************************************************************************}
  181. {* FixProcessListMenus - Undim menu items for process list window}
  182. {*}
  183. {* Any menus that should be available when the process list window is in front}
  184. {* are undimmed if the current conditions are appropriate.}
  185. {*******************************************************************************)
  186.  
  187.     procedure FixProcessListMenus (processListWindow: WindowPtr);
  188.  
  189.  
  190. (*******************************************************************************}
  191. {* IsProcessInfoWindow - Is a WindowPtr a pointer to a process info window?}
  192. {*}
  193. {* When I want to find out whether a window that I have a pointer to is a process}
  194. {* info window or not, I call this routine.  It returns TRUE if aWindow is a}
  195. {* pointer to a process info window, FALSE if it isn’t.  If aWindow is NIL, then}
  196. {* IsProcessInfoWindow returns NIL.}
  197. {*******************************************************************************)
  198.  
  199.     function IsProcessInfoWindow (aWindow: WindowPtr): Boolean;
  200.  
  201.  
  202. (*******************************************************************************}
  203. {* IdleProcessInfoWindow - Give the specified process info window some idle time}
  204. {*}
  205. {* This routine is called once per event loop iteration.  It gives the specified}
  206. {* process info window some idle time to update the memory indicator.}
  207. {*******************************************************************************)
  208.  
  209.     procedure IdleProcessInfoWindow (processInfoWindow: WindowPtr);
  210.  
  211.  
  212. (*******************************************************************************}
  213. {* DrawProcessInfoWindow - Draw the contents of the process info window}
  214. {*}
  215. {* Whenever an update event is received for the process info window, the routine}
  216. {* is called to draw the static items into the window.}
  217. {*******************************************************************************)
  218.  
  219.     procedure DrawProcessInfoWindow (processInfoWindow: WindowPtr);
  220.  
  221.  
  222. (*******************************************************************************}
  223. {* FixProcessInfoMenus - Undim menu items for process information window}
  224. {*}
  225. {* Any menus that should be available when a process information window is in}
  226. {* front are undimmed if the current conditions are appropriate.}
  227. {*******************************************************************************)
  228.  
  229.     procedure FixProcessInfoMenus (processInfoWindow: WindowPtr);
  230.  
  231.  
  232. (*******************************************************************************}
  233. {* CloseProcessInfoWindow - Close a process info window}
  234. {*}
  235. {* The process info window specified by "processInfoWindow" is closed and all its}
  236. {* associated memory is deallocated.}
  237. {*******************************************************************************)
  238.  
  239.     procedure CloseProcessInfoWindow (processInfoWindow: WindowPtr);
  240.  
  241.  
  242. (*******************************************************************************}
  243. {* IdleAllProcessWindows - Give every open process window idle time}
  244. {*}
  245. {* IdleAllProcessWindows is called to give the process list window and all open}
  246. {* process info windows some idle time.  This routine is called once per main}
  247. {* event loop iteration.}
  248. {*******************************************************************************)
  249.  
  250.     procedure IdleAllProcessWindows;
  251.  
  252.  
  253. (*******************************************************************************}
  254. {* DoLaunchInFront - Launch a process to the front}
  255. {*}
  256. {* When the user wants to launch a process and have it brought to the front, this}
  257. {* routine is called.  It allows the user to choose a process through Standard}
  258. {* File.  That application is then launched and brought to the front.}
  259. {*******************************************************************************)
  260.  
  261.     procedure DoLaunchInFront;
  262.  
  263.  
  264. (*******************************************************************************}
  265. {* DoLaunchInBack - Launch a process to the back}
  266. {*}
  267. {* When the user wants to launch a process and have it sent to the back of all of}
  268. {* the open processes, this routine is called.  It allows the user to choose a}
  269. {* process through Standard File.  That process is then launched and sent to the}
  270. {* back.}
  271. {*******************************************************************************)
  272.  
  273.     procedure DoLaunchInBack;
  274.  
  275.  
  276. (*******************************************************************************}
  277. {* DoLaunchTo - Launch a process and terminate self}
  278. {*}
  279. {* When the user wants to launch a process and then immediately quit ProcDoggie,}
  280. {* this routine is called.  It allows the user to choose a process through}
  281. {* Standard File.  That process is then launched and brought to the front, and}
  282. {* then ProcDoggie is immediately terminated.}
  283. {*******************************************************************************)
  284.  
  285.     procedure DoLaunchTo;
  286.  
  287.  
  288. (*******************************************************************************}
  289. {* DoLaunchMode - Handle Simple Launch, Open on Launch, Print on Launch commands}
  290. {*}
  291. {* This routine is called when a launch mode menu item is chosen.  The launch}
  292. {* mode items are in the file menu and are the “Simple Launch,” “Open Documents}
  293. {* on Launch,” and “Print Documents on Launch” items.}
  294. {*******************************************************************************)
  295.  
  296.     procedure DoLaunchMode (modeItem: Integer);
  297.  
  298.  
  299. (*******************************************************************************}
  300. {* DoBringProcessToFront - Bring an open process to the front}
  301. {*}
  302. {* When the user chooses the Bring to Front menu item, this routine is called to}
  303. {* bring all the selected process to the front in the order that they appear in}
  304. {* the process list window specified by processListWindow.  For the moment, it}
  305. {* won’t bring it’s own application (ProcDoggie) to the front.  I don’t know how}
  306. {* or whether I’ll be able to fix that.}
  307. {*******************************************************************************)
  308.  
  309.     procedure DoBringProcessToFront (processListWindow: WindowPtr);
  310.  
  311.  
  312. (*******************************************************************************}
  313. {* DoGetProcessInfo - Get information about selected processes}
  314. {*}
  315. {* This routine is called when the user desires information about the processes}
  316. {* selected in the process list window specified by the "processListWindow"}
  317. {* parameter.}
  318. {*******************************************************************************)
  319.  
  320.     procedure DoGetProcessInfo (processListWindow: WindowPtr);
  321.  
  322.  
  323. (*******************************************************************************}
  324. {* DoTerminateProcess - Terminate the selected processes}
  325. {*}
  326. {* This routine is called when the user wants to terminate the processes selected}
  327. {* in the process list window specified by the "processListWindow" parameter.}
  328. {*******************************************************************************)
  329.  
  330.     procedure DoTerminateProcess (processListWindow: WindowPtr);
  331.  
  332.  
  333. { menu stuff }
  334. (*******************************************************************************}
  335. {* Constants}
  336. {*******************************************************************************)
  337.  
  338.     const
  339.         mApple = 128; {Menu ID and resource ID of Apple menu}
  340.         iAbout = 1;   {Menu item number of About SevenPaint item}
  341.  
  342.         mFile = 129; {Menu ID and resource ID of File menu}
  343.         iLaunchFore = 1;   {Menu item number of Launch to Foreground… item}
  344.         iLaunchBack = 2;   {Menu item number of Launch to Background… item}
  345.         iLaunchTo = 3;   {Menu item number of Launch To… item}
  346.         iJustLaunch = 5;   {Menu item number of Simple Launch item}
  347.         iOpenLaunch = 6;   {Menu item number of Open Documents on Launch item}
  348.         iPrintLaunch = 7;   {Menu item number of Print Documents on Launch item}
  349.         iQuit = 9;   {Menu item number of Quit item}
  350.  
  351.         mProcess = 130; {Menu ID and resource ID of Process menu}
  352.         iBringFront = 1;   {Menu item number of Bring Process to Front item}
  353.         iShowProcessInfo = 2;   {Menu item number of Show Process Info item}
  354.         iTerminateProcess = 3;   {Menu item number of Terminate Process item}
  355.  
  356.  
  357. (*******************************************************************************}
  358. {* StartMenus - Do additional initialization of the menus}
  359. {*}
  360. {* This routine is called just after calling the Utilities sample code routine,}
  361. {* StandardMenuSetup.  This application needs to do just a little bit of}
  362. {* additional initialization for menus.  See UMenuHandler.inc1.p for details.}
  363. {*}
  364. {* If there isn’t enough memory to load the menus, then the gError global is set}
  365. {* to memFullErr.  If desired menu resources couldn’t be found, then gError is}
  366. {* set to resNotFound.  If any other error occurs, then gError is set to}
  367. {* dsSysErr.}
  368. {*******************************************************************************)
  369.  
  370.     procedure StartMenus;
  371.  
  372.  
  373. (*******************************************************************************}
  374. {* DoMenuChoice - Dispatch to the appropriate routine for a menu choice}
  375. {*}
  376. {* When it’s determined that a menu item was chosen, this routine is called to}
  377. {* dispatch to the appropriate routine for the chosen menu item.  The menu item}
  378. {* and menu number returned by MenuSelect and MenuKey is passed in the menuChoice}
  379. {* parameter.}
  380. {*******************************************************************************)
  381.  
  382.     procedure DoMenuChoice (menuChoice: LongInt);
  383.  
  384.  
  385. (*******************************************************************************}
  386. {* FixMenus - Fix menus so that proper items are enabled and marked}
  387. {*}
  388. {* FixMenus is called to assure that menu items are disable, enabled, marked, and}
  389. {* unmarked appropriately.  It’s called at the end of every iteration of the main}
  390. {* event loop.}
  391. {*******************************************************************************)
  392.  
  393.     procedure FixMenus;
  394.  
  395. implementation
  396.  
  397.     const
  398.         rProcessListWindID = 128; {Resource ID of process list window template}
  399.         rProcessInfoWindID = 129; {Resource ID of process info window template}
  400.         rProcessInfoDitlID = 129; {Resource ID of process info dialog item list}
  401.  
  402.         kProcessListWindKind = 8;    {In windowKind field of process list windows}
  403.         kProcessInfoWindKind = 9;    {In windowKind field of process info windows}
  404.         kActivateList = TRUE; {Pass to LActivate to specify activate list}
  405.         kScrollBarWidth = 16;   {Width of scroll bar in pixels}
  406.  
  407.         rAppOrDAStringID = 128; {Resource ID of Application or DA string}
  408.         kAppStringInd = 1;   {Index for Application string}
  409.         kDAStringInd = 2;   {Index for Desk Accessory string}
  410.  
  411.         rCheckMarkID = 128; {Resource ID of checkmark string}
  412.  
  413.         kProcessNameItem = 1;  {Dialog item # of process name}
  414.         kAppOrDAItem = 2;  {Dialog item # of Application/DA string}
  415.         kTotalSizeItem = 5;  {Dialog item # of Total Size readout}
  416.         kFreeSpaceItem = 6;  {Dialog item # of Free Space readout}
  417.         kMemIndicatorItem = 7;  {Dialog item # of partition memory indicator}
  418.         kGrayLineItem0 = 8;  {Dialog item # of first gray line}
  419.         kTypeItem = 11; {Dialog item # of TYPE item}
  420.         kCreatorItem = 12; {Dialog item # of Creator item}
  421.         kGrayLineItem1 = 13; {Dialog item # of second gray line}
  422.         kSusResChkItem = 14; {Dialog item # of suspend/resume checkmark}
  423.         kWindActChkItem = 15; {Dialog item # of window activate checkmark}
  424.         kGetClickChkItem = 16; {Dialog item # of Get front click checkmark}
  425.         kAppDiedChkItem = 17; {Dialog item # of App Died checkmark}
  426.         kStationeryChkItem = 18; {Dialog item # of Stationery checkmark}
  427.         kCanBackChkItem = 19; {Dialog item # of Can Background checkmark}
  428.         kOnlyBackChkItem = 20; {Dialog item # of Only Background checkmark}
  429.         kHighLevelChkItem = 21; {Dialog item # of High-Level Evt checkmark}
  430.         kRHighLevelChkItem = 22; {Dialog item # of Remote High-Level checkmark}
  431.         kMultiUserChkItem = 23; {Dialog item # of Multi-user Launch checkmark}
  432.         k32BitCleanChkItem = 24; {Dialog item # of 32-Bit Clean checkmark}
  433.  
  434.         kUsedColor = 2; {Process Info window palette color for used memory}
  435.         kFreeColor = 3; {Process Info window palette color for free memory}
  436.  
  437.  
  438. (*******************************************************************************}
  439. {* Types}
  440. {*******************************************************************************)
  441.  
  442.     type
  443.         {Pointer to canonical format for number strings}
  444.         NumFormatStringPtr = ^NumFormatString;
  445.  
  446.         {Pointer to process serial number}
  447.         PSNPtr = ^ProcessSerialNumber;
  448.  
  449.  
  450. (*******************************************************************************}
  451. {* Variables}
  452. {*******************************************************************************)
  453.  
  454.     var
  455.         gLaunchMode: LaunchModeCode; {Open documents? Print documents?}
  456.  
  457.  
  458.  
  459. (*******************************************************************************}
  460. {* for Menu Stuff}
  461. {*******************************************************************************)
  462.     const
  463.         rMenuBar = 128; {Resource ID of this application’s MBAR resource}
  464.  
  465.         mFirst = mFile;    {Menu ID of the first non-Apple menu in the menu list}
  466.         mLast = mProcess; {Menu ID of the last menu in the menu list}
  467.  
  468.  
  469. (*******************************************************************************}
  470. {* Types}
  471. {*******************************************************************************)
  472.  
  473.     type
  474.         MenuGuide = record
  475.                 theMenu: MenuHandle; {Handle to this guide’s menu}
  476.                 enables: LongInt     {Current enable flags}
  477.             end;
  478.  
  479.  
  480. (*******************************************************************************}
  481. {* Variables}
  482. {*******************************************************************************)
  483.  
  484.     var
  485.         gMenuGuides: array[mFirst..mLast] of MenuGuide;
  486.  
  487.  
  488. {$S ProcessGuts}
  489. (*******************************************************************************}
  490. {* Public: SetLaunchMode}
  491. {*}
  492. {* The global variable, "gLaunchMode", is set to the launch mode specified by}
  493. {* "newMode".}
  494. {*******************************************************************************)
  495.  
  496.     procedure SetLaunchMode (newMode: LaunchModeCode);
  497.  
  498.     begin
  499.         gLaunchMode := newMode
  500.     end;
  501.  
  502.  
  503. {$S ProcessGuts}
  504. (*******************************************************************************}
  505. {* Public: GetLaunchMode}
  506. {*}
  507. {* The value of the global variable, "gLaunchMode", is returned.}
  508. {*******************************************************************************)
  509.  
  510.     function GetLaunchMode: LaunchModeCode;
  511.  
  512.     begin
  513.         GetLaunchMode := gLaunchMode
  514.     end;
  515.  
  516.  
  517. {$S Main}
  518. (*******************************************************************************}
  519. {* Public: IsProcessListWindow}
  520. {*}
  521. {* I store a unique code in the windowKind field of every window I create so that}
  522. {* I can identify the kind of window it is later… like now!  I check to see if}
  523. {* the windowKind field of aWindow is kProcessListWindKind or not.  If it is, I}
  524. {* know it’s a process list window, and so IsProcessListWindow returns TRUE.}
  525. {*******************************************************************************)
  526.  
  527.     function IsProcessListWindow (aWindow: WindowPtr): Boolean;
  528.  
  529.     begin
  530.         if aWindow <> nil then
  531.             IsProcessListWindow := WindowPeek(aWindow)^.windowKind = kProcessListWindKind
  532.         else
  533.             IsProcessListWindow := FALSE
  534.     end;
  535.  
  536.  
  537. {$S ProcessGuts}
  538. (*******************************************************************************}
  539. {* Public: CreateProcessListWindow}
  540. {*}
  541. {* I store the constant kProcessListWindKind into the windowKind field of the new}
  542. {* window.  When the routine IsProcessListWindow is called, it uses this field to}
  543. {* identify a window as a process list window.}
  544. {*}
  545. {* See the UWindowHandler unit for code to create a new window.}
  546. {*******************************************************************************)
  547.  
  548.     function CreateProcessListWindow: WindowPtr;
  549.  
  550.         const
  551.             kDrawList = TRUE; {Pass to LNew; list must be drawn immediately}
  552.             kHasGrow = TRUE; {Pass to LNew; list has grow box}
  553.             kHasHorzScroll = TRUE; {Pass to LNew; list has a horizontal scroll bar}
  554.             kHasVertScroll = TRUE; {Pass to LNew; list has a vertical scroll bar}
  555.  
  556.         var
  557.             aWindow: WindowPtr;  {Pointer to the process list window}
  558.             processList: ListHandle; {Handle to the list of processes}
  559.             listRect: Rect;       {Rectangle of list in window coords}
  560.             listDimensions: Rect;       {Dimensions of list in cells}
  561.             cellSize: Point;      {Size of cell in pixels}
  562.             currFont: FontInfo;   {Information about current port’s font}
  563.  
  564.         procedure HandleError (messageClass: Integer; messageIndex: Integer);
  565.  
  566.             var
  567.                 result: Integer; {Result of alert; ignored}
  568.  
  569.         begin
  570.             if aWindow <> nil then
  571.                 begin
  572.                     CloseWindow(aWindow);
  573.                     DisposPtr(Ptr(aWindow))
  574.                 end;
  575.             result := ShowStopAlert(messageClass, messageIndex);
  576.             gError := noErr;
  577.             CreateProcessListWindow := nil;
  578.             EXIT(CreateProcessListWindow)
  579.         end;
  580.  
  581.     begin
  582.         aWindow := nil;
  583.  
  584.         (* Create the new window *)
  585.         aWindow := CreateWindow(rProcessListWindID);
  586.         if gError <> noErr then
  587.             if gError = memFullErr then
  588.                 HandleError(rMemErrMessages, kMemErrProcListOpenMsg)
  589.             else if gError = resNotFound then
  590.                 HandleError(rResErrMessages, kResErrAppDamageMsg)
  591.             else if gError = dsSysErr then
  592.                 HandleError(rMiscErrMessages, kMiscErrUnknownMsg);
  593.  
  594.         (* Set up the window *)
  595.         SetPort(aWindow);
  596.         WindowPeek(aWindow)^.windowKind := kProcessListWindKind;
  597.         TextFont(1);
  598.  
  599.         (* Create the process list *)
  600.         GetFontInfo(currFont);(*<*)
  601.         listRect := aWindow^.portRect;
  602.         listRect.right := listRect.right - kScrollBarWidth + 1;
  603.         SetRect(listDimensions, 0, 0, 1, 0);(*<*)
  604.         cellSize.h := listRect.right - listRect.left;
  605.         cellSize.v := currFont.ascent + currFont.descent + currFont.leading;
  606.         processList := LNew(listRect, listDimensions, cellSize, 128, aWindow, kDrawList, not kHasGrow, not kHasHorzScroll, kHasVertScroll);
  607.         if FailLowMemory(0) then
  608.             HandleError(rMemErrMessages, kMemErrProcListOpenMsg);
  609.  
  610.         (* Make sure the list is activated *)
  611.         LActivate(kActivateList, processList);
  612.  
  613.         (* Save a handle to the list in the refCon of the window *)
  614.         SetWRefCon(aWindow, LongInt(processList));
  615.  
  616.         (* Set the new window as the current GrafPort and return *)
  617.         SetPort(aWindow);
  618.         CreateProcessListWindow := aWindow
  619.     end;
  620.  
  621.  
  622. {$S ProcessGuts}
  623. (*******************************************************************************}
  624. {* Private: EqualPSN - List Manager search proc}
  625. {*}
  626. {* The List Manager’s LSearch function can take a pointer to a routine that}
  627. {* checks to see if a record matches an entry in the list.  The routine must have}
  628. {* an interface identical to IUMagIDString.  EqualPSN is the routine that I pass}
  629. {* to LSearch in the IdleProcessListWindow routine.  It compares the process}
  630. {* serial number passed in testPSN against the process serial number contained in}
  631. {* the ProcessListInfoRec of a cell.  Because I already know the lengths of}
  632. {* ProcessListInfoRecs and ProcessSerialNumber records, I ignore the aLen and}
  633. {* bLen parameters.}
  634. {*}
  635. {* If the two process serial numbers refer to the same process, then EqualPSN}
  636. {* returns 0, otherwise it returns 1.}
  637. {*******************************************************************************)
  638.  
  639.     function EqualPSN (processInfo: ProcessListInfoPtr; testPSN: ProcessSerialNumberPtr; aLen: Integer; bLen: Integer): Integer;
  640.  
  641.         var
  642.             equal: Boolean; {TRUE if PSNs are equal}
  643.             error: OSErr;
  644.  
  645.     begin
  646.         error := SameProcess(testPSN^, processInfo^.serialNumber, equal); (*<*)
  647.         if equal then
  648.             EqualPSN := 0
  649.         else
  650.             EqualPSN := 1
  651.     end;
  652.  
  653.  
  654. {$S ProcessGuts}
  655. (*******************************************************************************}
  656. {* Private: SetProcessListInfo - Set process list cell info}
  657. {*}
  658. {* SetProcessListInfo sets the cell specified by row (I’m only using one column,}
  659. {* so only the row matters) of the list specified by procList to the information}
  660. {* in procInfo.  My lists contain ProcessListInfoRecs, which contain only two of}
  661. {* the fields in ProcessInfoRecs (process name and process serial number), so I}
  662. {* just copy these two fields from procInfo into listInfo.  I then use LSetCell}
  663. {* to copy listInfo into the list.}
  664. {*******************************************************************************)
  665.  
  666.     procedure SetProcessListInfo (procInfo: ProcessInfoRec; row: Integer; procList: ListHandle);
  667.  
  668.         var
  669.             listInfo: ProcessListInfoRec; {Process info from List Mgr list}
  670.             newCell: Cell;               {Cell in which to set information}
  671.             result: Integer;            {Result of alert; ignored}
  672.  
  673.     begin
  674.         (* Copy the process name *)
  675.         BlockMove(Ptr(procInfo.processName), @listInfo.processName, ORD(procInfo.processName^[0]) + 1);
  676.  
  677.         (* Copy the process serial number *)
  678.         listInfo.serialNumber := procInfo.processNumber;
  679.  
  680.         (* Set the specified cell to the new ProcessListInfoRec *)
  681.         newCell.h := 0;
  682.         newCell.v := row;
  683.         LSetCell(Ptr(@listInfo), SIZEOF(ProcessListInfoRec), newCell, procList)
  684.     end;
  685.  
  686.  
  687. {$S ProcessGuts}
  688. (*******************************************************************************}
  689. {* Public: IdleProcessListWindow}
  690. {*}
  691. {* I’m using a simple algorithm to keep the process list window’s process list}
  692. {* updated to the Process Manager’s process list, but it’ll probably be tough to}
  693. {* describe.  Here goes. . .}
  694. {*}
  695. {* I compare the process serial number of each entry in the Process Manager’s}
  696. {* list against the process serial number of the corresponding entry in}
  697. {* the process list window’s list.  If they match, then I just go on to the next}
  698. {* entries of the lists.  If they don’t match, then I search the window’s list in}
  699. {* case the matching process is farther down.  If I do find it farther down, then}
  700. {* I assume that the processes in the window’s list that come between the}
  701. {* matching entries in the two lists were deleted.  So, I delete those rows.  If}
  702. {* I don’t find it farther down, then I assume that the entry is new.  I then}
  703. {* insert a new row in the corresponding position of the window’s list and copy}
  704. {* the process information to it.}
  705. {*}
  706. {* If I run out of rows in the window’s list before getting through the entire}
  707. {* Process Manager list, then I just keep adding new rows to the end of the}
  708. {* window’s list and copying over the balance.}
  709. {*}
  710. {* If I go through the entire Process Manager list but there are left-over}
  711. {* entries in the window’s list, then I just delete those left-overs.}
  712. {*}
  713. {* So, that’s the algorithm.  It was the most efficient one I could come up with}
  714. {* that wasn’t even harder to explain.  Beware: some parts of this routine have}
  715. {* only gotten minimal testing, so I wouldn’t be surprised if you find bugs.}
  716. {*******************************************************************************)
  717.  
  718.     procedure IdleProcessListWindow (processListWindow: WindowPtr);
  719.  
  720.         var
  721.             procNum: ProcessSerialNumber; {Serial number of open processes}
  722.             procInfo: ProcessInfoRec;      {Process info from Proc Mgr list}
  723.             procName: Str31;               {Name of the process}
  724.             listInfo: ProcessListInfoRec;  {Process info from List Mgr list}
  725.             listInfoLength: Integer;             {Size of ProcessListInfoRec}
  726.             currCell: Cell;                {List cell being checked}
  727.             matchCell: Cell;                {Cell with matching PSN}
  728.             procList: ListHandle;          {Handle to List Mgr process list}
  729.             foundMatch: Boolean;             {Found matching List Mgr entry}
  730.             equal: Boolean;             {Proc and List Mgr elements match}
  731.             result: Integer;             {Result of alert; ignored}
  732.             addedProcess: Boolean;             {TRUE if a process added to list}
  733.             error: OSErr;
  734.  
  735.     begin
  736.         (* Get the List Manager’s copy of the process list *)
  737.         procList := ListHandle(GetWRefCon(processListWindow));
  738.  
  739.         (* Start checking from start of List Mgr and Process Mgr lists *)
  740.         addedProcess := FALSE;
  741.         currCell.v := 0;
  742.         currCell.h := 0;
  743.         procNum.highLongOfPSN := 0;
  744.         procNum.lowLongOfPSN := kNoProcess;
  745.  
  746.         (* Keep looping through each open process *)
  747.         while GetNextProcess(procNum) = noErr do(*◊*)
  748.             begin
  749.                 (* Get information about an open process *)
  750.                 procInfo.processInfoLength := SIZEOF(ProcessInfoRec);
  751.                 procInfo.processName := @procName;
  752.                 procInfo.processAppSpec := nil;
  753.                 error := GetProcessInformation(procNum, procInfo); (*◊*)
  754.  
  755.                 (* Cmp List Mgr & Proc Mgr lists if enuf cells for # of processes *)
  756.                 if PtInRect(currCell, procList^^.dataBounds) then
  757.                     begin
  758.                         (* Get process info from List Mgr list *)
  759.                         listInfoLength := SIZEOF(ProcessListInfoRec);
  760.                         LGetCell(@listInfo, listInfoLength, currCell, procList);(*<*)
  761.  (*◊*)
  762.  
  763.                         (* If Proc & List Mgr lists differ, update List Mgr list *)
  764.                         error := SameProcess(procInfo.processNumber, listInfo.serialNumber, equal); (*<*)
  765.                         if not equal then
  766.                             begin
  767.                                 (* See if matching process farther down List Mgr list *)
  768.                                 matchCell := currCell;
  769.                                 foundMatch := LSearch(@procInfo.processNumber, SIZEOF(ProcessSerialNumber), @EqualPSN, matchCell, procList);
  770.                                         (*◊*)
  771.  
  772.                                 (* Was there a match farther down the List Mgr list? *)
  773.                                 if foundMatch then
  774.                                     (* Yes, delete intervening cells *)
  775.                                     LDelRow(matchCell.v - currCell.v, currCell.v, procList)
  776.                                 else
  777.                                     (* No, insert the new process into List Mgr list *)
  778.                                     begin
  779.                                         currCell.v := LAddRow(1, currCell.v, procList);
  780.                                         SetProcessListInfo(procInfo, currCell.v, procList)
  781.                                     end
  782.                             end
  783.                     end
  784.                 else
  785.                     begin
  786.                         (* Ran out of rows, add one *)
  787.                         currCell.v := LAddRow(1, currCell.v, procList);
  788.                         addedProcess := TRUE;
  789.  
  790.                         (* Set the new row to the new process information *)
  791.                         SetProcessListInfo(procInfo, currCell.v, procList)
  792.                     end;
  793.  
  794.                 (* Go to the next cell element in List Mgr list *)
  795.                 currCell.v := SUCC(currCell.v)
  796.             end;
  797.  
  798.         (* Delete any extraneous cells *)
  799.         if currCell.v < procList^^.dataBounds.bottom then
  800.             LDelRow(procList^^.dataBounds.bottom - currCell.v, currCell.v, procList);
  801.  
  802.         (* If added processes to the list and memory low, warn *)
  803.         if addedProcess and FailLowMemory(0) then
  804.             result := ShowCautionOKAlert(rMemErrMessages, kMemErrLowMemWarnMsg)
  805.     end;
  806.  
  807.  
  808. {$S ProcessGuts}
  809. (*******************************************************************************}
  810. {* Public: DrawProcessListWindow}
  811. {*}
  812. {* Not much here to explain.}
  813. {*******************************************************************************)
  814.  
  815.     procedure DrawProcessListWindow (processListWindow: WindowPtr);
  816.  
  817.         var
  818.             procList: ListHandle; {Handle to List Mgr process list}
  819.  
  820.     begin
  821.         SetPort(processListWindow);
  822.  
  823.         (* Get the List Manager’s copy of the process list *)
  824.         procList := ListHandle(GetWRefCon(processListWindow));
  825.  
  826.         (* Update the list *)
  827.         TextFont(1);
  828.         TextFace([]);
  829.         TextSize(GetDefFontSize);
  830.         EraseRect(processListWindow^.portRect);
  831.         LUpdate(processListWindow^.visRgn, procList);
  832.     end;
  833.  
  834.  
  835. {$S ProcessGuts}
  836. (*******************************************************************************}
  837. {* Public: ClickProcessListWindow}
  838. {*}
  839. {* The List Manager is doing the lion’s share of the work.}
  840. {*******************************************************************************)
  841.  
  842.     procedure ClickProcessListWindow (processListWindow: WindowPtr; clickEvent: EventRecord);
  843.  
  844.         var
  845.             procList: ListHandle; {Handle to List Mgr process list}
  846.             clickPos: Point;      {Position of mouse click in window coords}
  847.             doubleClick: Boolean;    {TRUE if cell was double-clicked}
  848.  
  849.     begin
  850.         SetPort(processListWindow);
  851.  
  852.         (* Get the List Manager’s copy of the process list *)
  853.         procList := ListHandle(GetWRefCon(processListWindow));
  854.  
  855.         (* Call the List Manager to handle the click *)
  856.         clickPos := clickEvent.where;
  857.         GlobalToLocal(clickPos);(*◊*)
  858.         doubleClick := LClick(clickPos, clickEvent.modifiers, procList);
  859.         if doubleClick then
  860.             DoBringProcessToFront(processListWindow);
  861.     end;
  862.  
  863.  
  864. {$S ProcessGuts}
  865. (*******************************************************************************}
  866. {* Public: ActivateProcessListWindow}
  867. {*}
  868. {* The List Manager is called to activate/deactivate the process list window.}
  869. {*******************************************************************************)
  870.  
  871.     procedure ActivateProcessListWindow (processListWindow: WindowPtr; becomingActive: Boolean);
  872.  
  873.         var
  874.             procList: ListHandle; {Handle to List Mgr process list}
  875.  
  876.     begin
  877.         SetPort(processListWindow);
  878.  
  879.         (* Get the List Manager’s copy of the process list *)
  880.         procList := ListHandle(GetWRefCon(processListWindow));
  881.  
  882.         (* Call the List Manager to activate or deactivate the list *)
  883.         LActivate(becomingActive, procList)
  884.     end;
  885.  
  886.  
  887. {$S ProcessGuts}
  888. (*******************************************************************************}
  889. {* Public: FixProcessListMenus}
  890. {*}
  891. {* The three launching items in the File menu are enabled as long as there’s}
  892. {* enough memory available.}
  893. {*}
  894. {* The List Manager routine, LGetSelect, is called to see if there are any}
  895. {* processes in the Process List window specified by the "processListWindow"}
  896. {* parameter that are selected.  If there are, then the three items in the}
  897. {* Process menu are enabled.  If there isn’t enough memory to safely work in,}
  898. {* then only the Bring Process to Front is enabled.}
  899. {*******************************************************************************)
  900.  
  901.     procedure FixProcessListMenus (processListWindow: WindowPtr);
  902.  
  903.         const
  904.             kFindNext = TRUE; {Pass to LGetSelect to find sequence of selections}
  905.  
  906.         var
  907.             aMenu: MenuHandle; {Handle to any menu we’re checking on}
  908.             procList: ListHandle; {Handle to List Mgr process list}
  909.             aCell: Cell;       {Cell of process list}
  910.  
  911.     begin
  912.         (* Get the List Manager’s copy of the process list *)
  913.         procList := ListHandle(GetWRefCon(processListWindow));
  914.  
  915.         (* Enable the File menu launch items *)
  916.         aMenu := GetMHandle(mFile);
  917.         if not FailLowMemory(0) then
  918.             begin
  919.                 EnableItem(aMenu, iLaunchFore);
  920.                 EnableItem(aMenu, iLaunchBack);
  921.                 EnableItem(aMenu, iLaunchTo)
  922.             end;
  923.  
  924.         (* Undim the Process menu items *)
  925.         aMenu := GetMHandle(mProcess);
  926.         aCell.v := 0;
  927.         aCell.h := 0;
  928.         if LGetSelect(kFindNext, aCell, procList) then (*◊*)
  929.             begin
  930.                 (* There’s ≥ 1 sel’d process, enable Bring Process to Front *)
  931.                 EnableItem(aMenu, iBringFront);
  932.  
  933.                 (* Only enable other two items if enough memory to safely work *)
  934.                 if not FailLowMemory(0) then
  935.                     begin
  936.                         EnableItem(aMenu, iShowProcessInfo);
  937.                         EnableItem(aMenu, iTerminateProcess)
  938.                     end
  939.             end
  940.     end;
  941.  
  942.  
  943. {$S Main}
  944. (*******************************************************************************}
  945. {* Public: IsProcessInfoWindow}
  946. {*}
  947. {* I store a unique code in the windowKind field of every window I create so that}
  948. {* I can identify the kind of window it is later… like now!  I check to see if}
  949. {* the windowKind field of aWindow is kProcessInfoWindKind or not.  If it is, I}
  950. {* know it’s a process info window, and so IsProcessInfoWindow returns TRUE.}
  951. {*******************************************************************************)
  952.  
  953.     function IsProcessInfoWindow (aWindow: WindowPtr): Boolean;
  954.  
  955.     begin
  956.         if aWindow <> nil then
  957.             IsProcessInfoWindow := WindowPeek(aWindow)^.windowKind = kProcessInfoWindKind
  958.         else
  959.             IsProcessInfoWindow := FALSE
  960.     end;
  961.  
  962.  
  963. {$S ProcessGuts}
  964. (*******************************************************************************}
  965. {* Private: GetNumberParts - Get the default number parts table}
  966. {*}
  967. {* To use the Script Manager’s number conversion routines, the number parts table}
  968. {* in the 'itl4' resource must be retrieved.  This routine gets the itl4 resource}
  969. {* and copies the number parts table into the "partsTable" parameter.}
  970. {*}
  971. {* If the retrieval was successful, then TRUE is returned.  If the itl4 resource}
  972. {* couldn’t be loaded for some reason, then FALSE is returned.}
  973. {*******************************************************************************)
  974.  
  975.     function GetNumberParts (var partsTable: NumberParts): Boolean;
  976.  
  977.         var
  978.             intl4: Itl4Handle; {Handle to the itl4 resource}
  979.  
  980.     begin
  981.         intl4 := Itl4Handle(IUGetIntl(4));
  982.         if intl4 <> nil then
  983.             begin
  984.                 partsTable := NumberPartsPtr(ORD(intl4^) + intl4^^.defPartsOffset)^;
  985.                 GetNumberParts := TRUE
  986.             end
  987.         else
  988.             GetNumberParts := FALSE
  989.     end;
  990.  
  991.  
  992. {$S ProcessGuts}
  993. (*******************************************************************************}
  994. {* Private: TextLineBox - Draw a line of text into a box.}
  995. {*}
  996. {* This routine is very similar to TextEdit’s TextBox routine, and in fact it}
  997. {* takes the same parameters.  But TextLineBox draws a single line of text}
  998. {* specified by "textLine" and having the length specified by "length" into}
  999. {* the current GrafPort, ignoring carriage returns and word-wrap.  This means}
  1000. {* that there’s less overhead than TextBox.  But TextBox itself is optimized for}
  1001. {* single lines of text, so there is an ulterior motive for this routine.}
  1002. {* TextBox erases the entire box before drawing the text.  This results in a}
  1003. {* slight flicker if TextBox is called to draw over previous text.  TextLineBox}
  1004. {* only erases the part of the box that isn’t covered with the text specified by}
  1005. {* "textLine".  Also, the text is drawn in srcCopy mode.  If TextLineBox is}
  1006. {* called to draw over existing text, the result should be a smooth transition}
  1007. {* from one text to another, without flicker.}
  1008. {*******************************************************************************)
  1009.  
  1010.     procedure TextLineBox (textLine: Ptr; length: Integer; box: Rect; just: Integer);
  1011.  
  1012.         var
  1013.             currPort: GrafPtr;   {Pointer to the current GrafPort}
  1014.             currTextMode: Integer;   {Current text mode}
  1015.             currFont: FontInfo;  {Current font information}
  1016.             lineWidth: Integer;   {Width of line of text in pixels}
  1017.             spareSpace: Integer;   {Width of box - width of text}
  1018.             spareRect: Rect;      {Rectangle of area not filled with text}
  1019.             currClip: RgnHandle; {Handle to the current clip region}
  1020.  
  1021.     begin
  1022.         (* Save the current clip region and set the clip region to "box" *)
  1023.         currClip := NewRgn;
  1024.         GetClip(currClip);(*<*)
  1025.         ClipRect(box);
  1026.  
  1027.         (* Save the current text mode and set it to srcCopy *)
  1028.         GetPort(currPort);(*<*)
  1029.         currTextMode := currPort^.txMode;
  1030.         TextMode(srcCopy);
  1031.  
  1032.         (* If default justification, set to real justification based on SysJust *)
  1033.         if just = teFlushDefault then
  1034.             if GetSysJust = 0 then
  1035.                 just := teFlushLeft
  1036.             else
  1037.                 just := teFlushRight;
  1038.  
  1039.         (* Move pen to baseline on left side of box *)
  1040.         GetFontInfo(currFont);
  1041.         MoveTo(box.left, box.top + currFont.ascent);
  1042.  
  1043.         (* Find the width of the specified text *)
  1044.         lineWidth := TextWidth(textLine, 0, length);
  1045.  
  1046.         (* Adjust the pen for centered or right-aligned text *)
  1047.         if just <> teFlushLeft then
  1048.             begin
  1049.                 spareSpace := box.right - box.left - lineWidth;
  1050.                 if just = teCenter then
  1051.                     spareSpace := spareSpace div 2;
  1052.                 Move(spareSpace, 0);
  1053.             end;
  1054.  
  1055.         (* Erase area at end(s) of text *)
  1056.         spareRect := box;
  1057.         if just = teFlushLeft then
  1058.             spareRect.left := spareRect.left + lineWidth
  1059.         else
  1060.             begin
  1061.                 if just = teCenter then
  1062.                     begin
  1063.                         spareRect.left := spareRect.left + spareSpace + lineWidth;
  1064.                         EraseRect(spareRect)
  1065.                     end;
  1066.                 spareRect.left := box.left;
  1067.                 spareRect.right := spareRect.left + spareSpace;
  1068.             end;
  1069.         if not EmptyRect(spareRect) then
  1070.             EraseRect(spareRect);
  1071.  
  1072.         (* Draw the line of text *)
  1073.         DrawText(textLine, 0, length);
  1074.  
  1075.         (* Restore the port to its normal state *)
  1076.         TextMode(currTextMode);
  1077.         SetClip(currClip);
  1078.         DisposeRgn(currClip)
  1079.     end;
  1080.  
  1081.  
  1082. {$S ProcessGuts}
  1083. (*******************************************************************************}
  1084. {* Private: FindProcessInfoWindow - Find a process info window for a process}
  1085. {*}
  1086. {* This routine searches the window list for a process info window that}
  1087. {* represents the process with the process serial number specified by}
  1088. {* "searchPSN".  Every process info window has a handle to the process serial}
  1089. {* number of the process it represents in the refCon field of the window.  The}
  1090. {* Process Manager routine, SameProcess, does the work of comparing the given}
  1091. {* process serial number against the process serial number in the refCon.}
  1092. {*}
  1093. {* If a window for the specified process is found, a pointer to that window is}
  1094. {* returned.  If there isn’t any window representing the given process, then NIL}
  1095. {* is returned.}
  1096. {*******************************************************************************)
  1097.  
  1098.     function FindProcessInfoWindow (searchPSN: ProcessSerialNumber): WindowPtr;
  1099.  
  1100.         var
  1101.             testWindow: WindowPtr; {Pointer to window we’re testing}
  1102.             found: Boolean;   {TRUE if matching process info window was found}
  1103.             psnHandle: Handle;    {Handle to PSN of window’s process info window}
  1104.             error: OSErr;
  1105.  
  1106.     begin
  1107.         found := FALSE;
  1108.         testWindow := FrontWindow;
  1109.  
  1110.         (* Loop until the window is found or every window has been searched *)
  1111.         while (testWindow <> nil) and (not found) do
  1112.             begin
  1113.                 if IsProcessInfoWindow(testWindow) then
  1114.                     begin
  1115.                         (* Get the PSN of the window from its refCon *)
  1116.                         psnHandle := Handle(GetWRefCon(testWindow));
  1117.  
  1118.                         (* Compare window’s PSN against searchPSN *)
  1119.                         HLock(psnHandle);
  1120.                         error := SameProcess(searchPSN, PSNPtr(psnHandle^)^, found);
  1121.                                 (*<*)
  1122.                         HUnlock(psnHandle)
  1123.                     end;
  1124.  
  1125.                 (* Go to the next window in the window list *)
  1126.                 if not found then
  1127.                     testWindow := WindowPtr(WindowPeek(testWindow)^.nextWindow)
  1128.             end;
  1129.  
  1130.         (* Return pointer to matching process info window, or NIL if no match *)
  1131.         FindProcessInfoWindow := testWindow
  1132.     end;
  1133.  
  1134.  
  1135. {$S ProcessGuts}
  1136. (*******************************************************************************}
  1137. {* Private: CreateProcessInfoWindow - Create a process info window}
  1138. {*}
  1139. {* This routine is called to create a new process info window and to display it}
  1140. {* on the screen.  A pointer to the window is returned.  If there wasn’t enough}
  1141. {* memory to open the new window, or if there was some other problem preventing}
  1142. {* the window from being completely created, then an alert indicating the problem}
  1143. {* is presented to the user and NIL is returned.}
  1144. {*}
  1145. {* I store the constant kProcessInfoWindKind into the windowKind field of the new}
  1146. {* window.  When the routine IsProcessInfoWindow is called, it uses this field to}
  1147. {* identify a window as a process info window.}
  1148. {*******************************************************************************)
  1149.  
  1150.     function CreateProcessInfoWindow: WindowPtr;
  1151.  
  1152.         var
  1153.             aWindow: WindowPtr; {Pointer to the new window}
  1154.             error: OSErr;
  1155.  
  1156.         procedure HandleError (messageClass: Integer; messageIndex: Integer);
  1157.  
  1158.             var
  1159.                 result: Integer; {Result of alert; ignored}
  1160.  
  1161.         begin
  1162.             if aWindow <> nil then
  1163.                 CloseProcessInfoWindow(aWindow);
  1164.             result := ShowStopAlert(messageClass, messageIndex);
  1165.             gError := noErr;
  1166.             CreateProcessInfoWindow := nil;
  1167.             EXIT(CreateProcessInfoWindow)
  1168.         end;
  1169.  
  1170.     begin
  1171.         aWindow := nil;
  1172.  
  1173.         (* Create the new window *)
  1174.         aWindow := CreateDialog(rProcessInfoWindID);
  1175.         if aWindow = nil then
  1176.             if gError = memFullErr then
  1177.                 HandleError(rMemErrMessages, kMemErrProcInfoOpenMsg)
  1178.             else if gError = resNotFound then
  1179.                 HandleError(rResErrMessages, kResErrAppDamageMsg)
  1180.             else if gError = dsSysErr then
  1181.                 HandleError(rMiscErrMessages, kMiscErrUnknownMsg);
  1182.  
  1183.         (* Set up the window *)
  1184.         SetPort(aWindow);
  1185.         WindowPeek(aWindow)^.windowKind := kProcessInfoWindKind;
  1186.  
  1187.         (* Install the dialog items *)
  1188.         error := InstallDialogItems(aWindow, rProcessInfoDitlID);
  1189.         if error <> noErr then
  1190.             if error = memFullErr then
  1191.                 HandleError(rMemErrMessages, kMemErrProcInfoOpenMsg)
  1192.             else if error = resNotFound then
  1193.                 HandleError(rResErrMessages, kResErrAppDamageMsg)
  1194.             else if error = dsSysErr then
  1195.                 HandleError(rMiscErrMessages, kMiscErrUnknownMsg);
  1196.  
  1197.         CreateProcessInfoWindow := aWindow
  1198.     end;
  1199.  
  1200.  
  1201. {$S ProcessGuts}
  1202. (*******************************************************************************}
  1203. {* Private: DrawGrayLine - Draw a gray line into a dialog item}
  1204. {*}
  1205. {* DrawGrayLine draws a line from the top-left corner of "grayLineRect" to its}
  1206. {* bottom-right corner.  On a non-Color QuickDraw Macintosh, this line is simply}
  1207. {* drawn using the 50% gray pattern.  On a Color QuickDraw Macintosh, a gray}
  1208. {* type-2 pattern is created with a gray color.  When this pattern is used to}
  1209. {* draw to the screen, it is drawn using the specified color if possible.  If}
  1210. {* there aren’t enough available colors, the color is dithered using the closest}
  1211. {* available colors.}
  1212. {*******************************************************************************)
  1213.  
  1214.     procedure DrawGrayLine (grayLineRect: Rect);
  1215.  
  1216.         var
  1217.             qdVersion: LongInt;      {QuickDraw version number}
  1218.             grayColor: RGBColor;     {Color of gray line}
  1219.             grayPattern: PixPatHandle; {Handle to the gray pattern}
  1220.             result: OSErr;
  1221.  
  1222.     begin
  1223.         grayPattern := nil;
  1224.         PenNormal;
  1225.  
  1226.         (* See if Color QuickDraw is on this machine or not *)
  1227.         result := Gestalt(gestaltQuickdrawVersion, qdVersion); (*<*)
  1228.         if qdVersion = gestaltOriginalQD then
  1229.             (* Nope, just draw a 50% gray pattern *)
  1230.             PenPat(gray)
  1231.         else
  1232.             (* Yup, make a true gray pattern that can be dithered to the screen *)
  1233.             begin
  1234.                 grayColor.red := $7FFF;
  1235.                 grayColor.green := $7FFF;
  1236.                 grayColor.blue := $7FFF;
  1237.                 grayPattern := NewPixPat;
  1238.                 MakeRGBPat(grayPattern, grayColor);
  1239.                 PenPixPat(grayPattern);
  1240.             end;
  1241.  
  1242.         (* Draw the line *)
  1243.         MoveTo(grayLineRect.left, grayLineRect.top);
  1244.         LineTo(grayLineRect.right, grayLineRect.bottom);
  1245.  
  1246.         (* Clean up *)
  1247.         if grayPattern <> nil then
  1248.             DisposPixPat(grayPattern);
  1249.         PenNormal
  1250.     end;
  1251.  
  1252.  
  1253. {$S ProcessGuts}
  1254. (*******************************************************************************}
  1255. {* Private: SetUpProcessInfoItems - Set up process information static text items}
  1256. {*}
  1257. {* This routine sets up the text of the static text items in the process info}
  1258. {* window specified by "processInfoWindow" to reflect the process information}
  1259. {* passed in the "processInfo" parameter.  Only the process information that}
  1260. {* doesn’t change while a process is active is set in this routine.  Information}
  1261. {* that changes while a process is active is set and drawn in the}
  1262. {* IdleProcessInfoWindow routine.}
  1263. {*}
  1264. {* Numbers are converted to strings using the FormatX2Str routine.  FormatX2Str}
  1265. {* requires a script-independent canonical number format so that the resulting}
  1266. {* string appears with the proper thousands separator regardless of the script}
  1267. {* in use.  I previously created a canonical number format that has the form}
  1268. {* ###,###,### in the U.S and saved it in a resource of type NUMF.}
  1269. {*******************************************************************************)
  1270.  
  1271.     procedure SetUpProcessInfoItems (processInfoWindow: WindowPtr; processInfo: ProcessInfoRec);
  1272.  
  1273.         var
  1274.             itemString: Str255;       {"Application" or "Desk Accessory" string}
  1275.             blankString: Integer;      {Dummy empty string}
  1276.             checkString: StringPtr;    {Ptr either to check mark or blankString}
  1277.             checkStrHnd: StringHandle; {Handle to check mark string}
  1278.             partitionSize: extended;     {Size of processes partition}
  1279.             partsTable: NumberParts;  {Number parts table from itl4 resource}
  1280.             canonRsrc: Handle;       {Hnd to canonical # format '###,###,###'}
  1281.             status: FormatStatus; {Status of #->String conversion}
  1282.             success: Boolean;      {TRUE if GetNumberParts call worked}
  1283.  
  1284.     begin
  1285.         (* Set process name *)
  1286.         SetStatTextItem(processInfoWindow, kProcessNameItem, @processInfo.processName^[1], ORD(processInfo.processName^[0]));
  1287.  
  1288.         (* Set Application or Desk Accessory string *)
  1289.         if BAND(processInfo.processMode, modeDeskAccessory) <> 0 then
  1290.             GetIndString(itemString, rAppOrDAStringID, kDAStringInd)(*◊*)
  1291.         else
  1292.             GetIndString(itemString, rAppOrDAStringID, kAppStringInd);(*◊*)
  1293.         SetStatTextItem(processInfoWindow, kAppOrDAItem, @itemString[1], ORD(itemString[0]));
  1294.  
  1295.         (* Set partition size item *)
  1296.         partitionSize := processInfo.processSize div 1024;
  1297.         success := GetNumberParts(partsTable);
  1298.         if success then
  1299.             begin
  1300.                 (* Get the canonical number format I created earlier *)
  1301.                 canonRsrc := Get1Resource('NUMF', 0);
  1302.                 if canonRsrc <> nil then
  1303.                     begin
  1304.                         (* Convert partition size from extended to formatted string *)
  1305.                         HLock(canonRsrc);
  1306.                         status := FormatX2Str(partitionSize, NumFormatStringPtr(canonRsrc^)^, partsTable, itemString);
  1307.                                 (*<*)
  1308.                         HUnlock(canonRsrc);
  1309.  
  1310.                         (* Set Total Size item to formatted partition size string *)
  1311.                         SetStatTextItem(processInfoWindow, kTotalSizeItem, @itemString[1], ORD(itemString[0]))
  1312.                     end
  1313.             end;
  1314.  
  1315.         (* Set type and creator *)
  1316.         SetStatTextItem(processInfoWindow, kTypeItem, @processInfo.processType, SIZEOF(LongInt));
  1317.         SetStatTextItem(processInfoWindow, kCreatorItem, @processInfo.processSignature, SIZEOF(OSType));
  1318.  
  1319.         (* Initialize the checkmark and blank strings *)
  1320.         checkStrHnd := GetString(rCheckMarkID);
  1321.         if checkStrHnd <> nil then
  1322.             BlockMove(Ptr(checkStrHnd^), @itemString, ORD(checkStrHnd^^[0]) + 1)
  1323.         else
  1324.             itemString[0] := CHR(0);
  1325.         blankString := 0;
  1326.  
  1327.         (* Check the suspend/resume flag *)
  1328.         if BAND(processInfo.processMode, modeNeedSuspendResume) <> 0 then
  1329.             checkString := @itemString
  1330.         else
  1331.             checkString := @blankString;
  1332.         SetStatTextItem(processInfoWindow, kSusResChkItem, @checkString^[1], ORD(checkString^[0]));
  1333.  
  1334.         (* Check the window activate flag *)
  1335.         if BAND(processInfo.processMode, modeDoesActivateOnFGSwitch) <> 0 then
  1336.             checkString := @itemString
  1337.         else
  1338.             checkString := @blankString;
  1339.         SetStatTextItem(processInfoWindow, kWindActChkItem, @checkString^[1], ORD(checkString^[0]));
  1340.  
  1341.         (* Check the window activate flag *)
  1342.         if BAND(processInfo.processMode, modeGetFrontClicks) <> 0 then
  1343.             checkString := @itemString
  1344.         else
  1345.             checkString := @blankString;
  1346.         SetStatTextItem(processInfoWindow, kGetClickChkItem, @checkString^[1], ORD(checkString^[0]));
  1347.  
  1348.         (* Check the window activate flag *)
  1349.         if BAND(processInfo.processMode, modeGetAppDiedMsg) <> 0 then
  1350.             checkString := @itemString
  1351.         else
  1352.             checkString := @blankString;
  1353.         SetStatTextItem(processInfoWindow, kAppDiedChkItem, @checkString^[1], ORD(checkString^[0]));
  1354.  
  1355.         (* Check the window activate flag *)
  1356.         if BAND(processInfo.processMode, modeStationeryAware) <> 0 then
  1357.             checkString := @itemString
  1358.         else
  1359.             checkString := @blankString;
  1360.         SetStatTextItem(processInfoWindow, kStationeryChkItem, @checkString^[1], ORD(checkString^[0]));
  1361.  
  1362.         (* Check the window activate flag *)
  1363.         if BAND(processInfo.processMode, modeCanBackground) <> 0 then
  1364.             checkString := @itemString
  1365.         else
  1366.             checkString := @blankString;
  1367.         SetStatTextItem(processInfoWindow, kCanBackChkItem, @checkString^[1], ORD(checkString^[0]));
  1368.  
  1369.         (* Check the window activate flag *)
  1370.         if BAND(processInfo.processMode, modeOnlyBackground) <> 0 then
  1371.             checkString := @itemString
  1372.         else
  1373.             checkString := @blankString;
  1374.         SetStatTextItem(processInfoWindow, kOnlyBackChkItem, @checkString^[1], ORD(checkString^[0]));
  1375.  
  1376.         (* Check the window activate flag *)
  1377.         if BAND(processInfo.processMode, modeHighLevelEventAware) <> 0 then
  1378.             checkString := @itemString
  1379.         else
  1380.             checkString := @blankString;
  1381.         SetStatTextItem(processInfoWindow, kHighLevelChkItem, @checkString^[1], ORD(checkString^[0]));
  1382.  
  1383.         (* Check the window activate flag *)
  1384.         if BAND(processInfo.processMode, modeLocalAndRemoteHLEvents) <> 0 then
  1385.             checkString := @itemString
  1386.         else
  1387.             checkString := @blankString;
  1388.         SetStatTextItem(processInfoWindow, kRHighLevelChkItem, @checkString^[1], ORD(checkString^[0]));
  1389.  
  1390.         (* Check the window activate flag *)
  1391.         if BAND(processInfo.processMode, modeMultiLaunch) <> 0 then
  1392.             checkString := @itemString
  1393.         else
  1394.             checkString := @blankString;
  1395.         SetStatTextItem(processInfoWindow, kMultiUserChkItem, @checkString^[1], ORD(checkString^[0]));
  1396.  
  1397.         (* Check the window activate flag *)
  1398.         if BAND(processInfo.processMode, mode32BitCompatible) <> 0 then
  1399.             checkString := @itemString
  1400.         else
  1401.             checkString := @blankString;
  1402.         SetStatTextItem(processInfoWindow, k32BitCleanChkItem, @checkString^[1], ORD(checkString^[0]));
  1403.  
  1404.     end;
  1405.  
  1406.  
  1407. {$S ProcessGuts}
  1408. (*******************************************************************************}
  1409. {* Public: IdleProcessInfoWindow}
  1410. {*}
  1411. {* The memory indicator and the free memory readout are updated with the current}
  1412. {* values.}
  1413. {*}
  1414. {* The free memory readout is a static text item in the DITL, but there’s no text}
  1415. {* for it.  Instead, I’m drawing into that item’s rectangle using TextLineBox.}
  1416. {* I set the item up as a static text item just so that I can specify the type}
  1417. {* characteristics of the free memory readout from the DITL resource rather than}
  1418. {* hard-coding them in this routine.}
  1419. {*}
  1420. {* Numbers are converted to strings using the FormatX2Str routine.  FormatX2Str}
  1421. {* requires a script-independent canonical number format so that the resulting}
  1422. {* string appears with the proper thousands separator regardless of the script}
  1423. {* in use.  I previously created a canonical number format that has the form}
  1424. {* ###,###,### in the U.S and saved it in a resource of type NUMF.}
  1425. {*******************************************************************************)
  1426.  
  1427.     procedure IdleProcessInfoWindow (processInfoWindow: WindowPtr);
  1428.  
  1429.         var
  1430.             processInfo: ProcessInfoRec; {Process info for window’s process}
  1431.             psnHandle: Handle;         {Handle to PSN of window’s process}
  1432.             freeSpace: extended;       {Amount of free space in partition}
  1433.             canonRsrc: Handle;         {canonical # format '###,###,###'}
  1434.             freeSpaceStr: Str255;         {String representation of freeSpace}
  1435.             status: FormatStatus;   {Status of #->string conversion}
  1436.             partsTable: NumberParts;    {Number parts table from itl4 resource}
  1437.             itemType: TypeInfoRec;    {Type information for free mem readout}
  1438.             itemRect: Rect;           {Rectangle of dialog item}
  1439.             freeAngle: Integer;        {Angle between free and full memory}
  1440.             aColor: RGBColor;       {Color to draw memory indicator}
  1441.             qdVersion: LongInt;        {Version of QuickDraw on this machine}
  1442.             success: Boolean;        {TRUE if GetNumberParts call worked}
  1443.             error: OSErr;
  1444.  
  1445.     begin
  1446.         SetPort(processInfoWindow);
  1447.         PenNormal;
  1448.  
  1449.         (* Get the PSN of the process associated with processInfoWindow *)
  1450.         psnHandle := Handle(GetWRefCon(processInfoWindow));
  1451.  
  1452.         (* Get information about an open process *)
  1453.         processInfo.processInfoLength := SIZEOF(ProcessInfoRec);
  1454.         processInfo.processName := nil;
  1455.         processInfo.processAppSpec := nil;
  1456.         HLock(psnHandle);
  1457.         error := GetProcessInformation(PSNPtr(psnHandle^)^, processInfo); (*◊*)
  1458.         HUnlock(psnHandle);
  1459.  
  1460.         (* Check to see whether the process still exists *)
  1461.         if error = procNotFound then
  1462.             (* Process terminated, so close this process info window *)
  1463.             CloseProcessInfoWindow(processInfoWindow)
  1464.         else
  1465.             begin
  1466.                 (* Starting here, convert amount of free space to a string *)
  1467.                 freeSpace := processInfo.processFreeMem div 1024;
  1468.  
  1469.                 (* Get number parts table from itl4 *)
  1470.                 success := GetNumberParts(partsTable);(*<*)
  1471.                 if success then
  1472.                     begin
  1473.                         (* Get my canonical number format *)
  1474.                         canonRsrc := Get1Resource('NUMF', 0);
  1475.                         if canonRsrc <> nil then
  1476.                             begin
  1477.                                 (* Convert free space to equivalent string *)
  1478.                                 HLock(canonRsrc);
  1479.                                 status := FormatX2Str(freeSpace, NumFormatStringPtr(canonRsrc^)^, partsTable, freeSpaceStr);
  1480.                                         (*<*)
  1481.                                 HUnlock(canonRsrc);
  1482.  
  1483.                                 (* Get the item rectangle of the free-space readout *)
  1484.                                 GetDialogItemRect(processInfoWindow, kFreeSpaceItem, itemRect);
  1485.                                         (*<*)
  1486.  
  1487.                                 (* Get the font characteristics of the stat text item *)
  1488.                                 GetStatTextFontInfo(processInfoWindow, kFreeSpaceItem, itemType);
  1489.                                         (*<*)
  1490.  
  1491.                                 (* Draw the free-space readout *)
  1492.                                 TextFont(itemType.typeFace);
  1493.                                 TextSize(itemType.typeSize);
  1494.                                 TextFace(itemType.typeStyle);
  1495.                                 TextLineBox(@freeSpaceStr[1], ORD(freeSpaceStr[0]), itemRect, itemType.textJust)
  1496.                             end;
  1497.                     end;
  1498.  
  1499.                 (* Draw the memory indicator frame *)
  1500.                 GetDialogItemRect(processInfoWindow, kMemIndicatorItem, itemRect);
  1501.                         (*<*)
  1502.                 FrameOval(itemRect);
  1503.                 InsetRect(itemRect, 1, 1);(*◊*)
  1504.  
  1505.                 (* Calc angle in the memory indicator that the free memory begins *)
  1506.                 freeAngle := processInfo.processFreeMem * 360 div processInfo.processSize;
  1507.  
  1508.                 (* Draw the memory indicator *)
  1509.                 error := Gestalt(gestaltQuickdrawVersion, qdVersion); (*<*)
  1510.                 if qdVersion = gestaltOriginalQD then
  1511.                     PenPat(black)
  1512.                 else
  1513.                     begin
  1514.                         PmForeColor(kUsedColor);
  1515.                         PmBackColor(0)
  1516.                     end;
  1517.  
  1518.                 (* Draw the used memory part of the memory indicator *)
  1519.                 PaintArc(itemRect, 0, 360 - freeAngle);
  1520.  
  1521.                 (* Set the color of the free memory part of the indicator *)
  1522.                 if qdVersion = gestaltOriginalQD then
  1523.                     PenPat(white)
  1524.                 else
  1525.                     begin
  1526.                         PmForeColor(kFreeColor);
  1527.                         PmBackColor(1)
  1528.                     end;
  1529.  
  1530.                 (* Draw the free memory part of the memory indicator *)
  1531.                 PaintArc(itemRect, 360 - freeAngle, freeAngle);
  1532.  
  1533.                 (* Reset the port characteristics back to normal *)
  1534.                 PenNormal;
  1535.                 if qdVersion <> gestaltOriginalQD then
  1536.                     begin
  1537.                         PmForeColor(1);
  1538.                         PmBackColor(0)
  1539.                     end
  1540.             end
  1541.     end;
  1542.  
  1543.  
  1544. {$S ProcessGuts}
  1545. (*******************************************************************************}
  1546. {* Public: DrawProcessInfoWindow}
  1547. {*}
  1548. {* The Dialog Utility routine, DrawDialogItems, is called to draw all the}
  1549. {* standard dialog items in the processInfoWindow specified by processInfoWindow.}
  1550. {* Then, the cosmetic gray lines are drawn.  The memory readouts aren’t drawn}
  1551. {* because they’re drawn in IdleProcessInfoWindow.}
  1552. {*******************************************************************************)
  1553.  
  1554.     procedure DrawProcessInfoWindow (processInfoWindow: WindowPtr);
  1555.  
  1556.         var
  1557.             grayLineRect: Rect; {Rectangle of gray line item}
  1558.  
  1559.     begin
  1560.         (* Draw the standard dialog items *)
  1561.         DrawDialogItems(processInfoWindow);
  1562.  
  1563.         (* Draw the two gray, cosmetic, separating lines *)
  1564.         GetDialogItemRect(processInfoWindow, kGrayLineItem0, grayLineRect); (*<*)
  1565.         DrawGrayLine(grayLineRect);
  1566.         GetDialogItemRect(processInfoWindow, kGrayLineItem1, grayLineRect); (*<*)
  1567.         DrawGrayLine(grayLineRect)
  1568.     end;
  1569.  
  1570.  
  1571. {$S ProcessGuts}
  1572. (*******************************************************************************}
  1573. {* Public: FixProcessInfoMenus}
  1574. {*}
  1575. {* If there’s enough memory to work with, the launch items in the File menu are}
  1576. {* enabled.}
  1577. {*******************************************************************************)
  1578.  
  1579.     procedure FixProcessInfoMenus (processInfoWindow: WindowPtr);
  1580.  
  1581.         var
  1582.             aMenu: MenuHandle; {Handle to any menu we’re checking on}
  1583.  
  1584.     begin
  1585.         (* Undim the File menu items *)
  1586.         aMenu := GetMHandle(mFile);
  1587.         if not FailLowMemory(0) then
  1588.             begin
  1589.                 EnableItem(aMenu, iLaunchFore);
  1590.                 EnableItem(aMenu, iLaunchBack);
  1591.                 EnableItem(aMenu, iLaunchTo)
  1592.             end
  1593.     end;
  1594.  
  1595.  
  1596. {$S ProcessGuts}
  1597. (*******************************************************************************}
  1598. {* Public: CloseProcessInfoWindow}
  1599. {*}
  1600. {* This should be pretty easy to figure out.}
  1601. {*******************************************************************************)
  1602.  
  1603.     procedure CloseProcessInfoWindow (processInfoWindow: WindowPtr);
  1604.  
  1605.         var
  1606.             psnHandle: Handle; {Handle to the PSN of process the window represents}
  1607.  
  1608.     begin
  1609.         DisposHandle(Handle(GetWRefCon(processInfoWindow)));
  1610.         CloseWindow(processInfoWindow);
  1611.         DisposeDialogItems(processInfoWindow);
  1612.         DisposPtr(Ptr(processInfoWindow))
  1613.     end;
  1614.  
  1615.  
  1616. {$S ProcessGuts}
  1617. (*******************************************************************************}
  1618. {* Public: IdleAllProcessWindows}
  1619. {*}
  1620. {* The process list window and process info windows each have their own idle}
  1621. {* routine defined in this source file, so the type of window is checked and the}
  1622. {* appropriate idle routine is called for that window.}
  1623. {*******************************************************************************)
  1624.  
  1625.     procedure IdleAllProcessWindows;
  1626.  
  1627.         var
  1628.             processWindow: WindowPtr; {Pointer to each process window being idled}
  1629.  
  1630.     begin
  1631.         processWindow := FrontWindow;
  1632.  
  1633.         (* Loop through all windows in the window list *)
  1634.         while processWindow <> nil do
  1635.             begin
  1636.                 (* Call the appropriate idle routine if it’s a process window *)
  1637.                 if IsProcessListWindow(processWindow) then
  1638.                     IdleProcessListWindow(processWindow)
  1639.                 else if IsProcessInfoWindow(processWindow) then
  1640.                     IdleProcessInfoWindow(processWindow);
  1641.  
  1642.                 (* Go to the next window in the window list *)
  1643.                 processWindow := WindowPtr(WindowPeek(processWindow)^.nextWindow)
  1644.             end
  1645.     end;
  1646.  
  1647.  
  1648. {$S ProcessGuts}
  1649. (*******************************************************************************}
  1650. {* Private: AppDAFilter - File filter procedure for apps and files with DAs}
  1651. {*}
  1652. {*     This is a Standard File file filter procedure that allows applications and}
  1653. {* any files with desk accessories in them to show up in the Standard File file}
  1654. {* list.}
  1655. {*}
  1656. {*     Checking to see whether a file is an application is easy enough.  Just}
  1657. {* check to see whether its type is APPL.  If it is, then it’s an application.}
  1658. {* Checking on desk accessories is trickier.  Desk accessories can be contained}
  1659. {* in any type of file.  So if a file isn’t doesn’t have the APPL type, I open}
  1660. {* the resource fork of the file using HOpenResFile and an access mode of}
  1661. {* fdRdPerm.  This allows me to open and close the resource file without worrying}
  1662. {* about that resource file being open by someone else because HOpenResFile with}
  1663. {* an access mode of fdRdPerm returns a unique access path to this routine.  When}
  1664. {* the file is open, I check for DRVR resources.  DRVR resources can be either}
  1665. {* desk accessories or device drivers.  I only want to show files containing desk}
  1666. {* accessories, so I check on the first character of the DRVR resource’s name.}
  1667. {* If it’s a null character, then the DRVR is a desk accessory.  If it’s any}
  1668. {* other character, then it’s a device driver and I ignore it.}
  1669. {*******************************************************************************)
  1670.  
  1671.     function AppDAFilter (fileInfo: CInfoPBPtr): Boolean;
  1672.  
  1673.         const
  1674.             kShowIt = FALSE; {FALSE means I do not filter out...}
  1675.  
  1676.         type
  1677.             LongIntPtr = ^LongInt;
  1678.  
  1679.         var
  1680.             resRef: Integer; {File ref num of file being tested}
  1681.             currResRef: Integer; {File ref number of current file}
  1682.             numDrvrs: Integer; {Number of DRVR resources in file being tested}
  1683.             index: Integer; {Index into resources of file being tested}
  1684.             drvrRsrc: Handle;  {Handle to DRVR resource; always NIL master ptr}
  1685.             resID: Integer; {Resource ID of DRVR resource; ignored}
  1686.             myResType: ResType; {Resource type of DRVR resource; ignored}
  1687.             resName: Str255;  {Resource name of DRVR resource}
  1688.  
  1689.     begin
  1690.         if fileInfo^.ioFlFndrInfo.fdType = 'APPL' then
  1691.             AppDAFilter := kShowIt
  1692.         else
  1693.             begin
  1694.                 (* Assume we don’t show the file *)
  1695.                 AppDAFilter := not kShowIt;
  1696.  
  1697.                 (* Want to check rsrcs, not load ’em, including preload resources *)
  1698.                 SetResLoad(FALSE);
  1699.  
  1700.                 (* Save current res file refnum, open the specified rsrc file *)
  1701.                 currResRef := CurResFile;
  1702.                 resRef := HOpenResFile(fileInfo^.ioVRefNum, LongIntPtr(CurDirStore)^, fileInfo^.ioNamePtr^, fsRdPerm);
  1703.  
  1704.                 (* If couldn’t open resource file, HOpenResFile returns -1 *)
  1705.                 if (resRef <> -1) then
  1706.                     begin
  1707.                         UseResFile(resRef);
  1708.  
  1709.                         (* Count number of DRVR resources in the file *)
  1710.                         numDrvrs := Count1Resources('DRVR');
  1711.                         if numDrvrs > 0 then
  1712.                             begin
  1713.                                 (* For each DRVR, see if it’s a DA *)
  1714.                                 for index := 1 to numDrvrs do
  1715.                                     begin
  1716.                                         drvrRsrc := Get1IndResource('DRVR', index);
  1717.                                         GetResInfo(drvrRsrc, resID, myResType, resName); (*<*)
  1718.  (*<*)
  1719.                                                 (*<*)
  1720.  
  1721.                                         (* If first char of name is null, it’s a DA *)
  1722.                                         if resName[1] = CHR(0) then
  1723.                                             AppDAFilter := kShowIt
  1724.                                     end
  1725.                             end;
  1726.                         CloseResFile(resRef)
  1727.                     end;
  1728.  
  1729.                 (* Restore everything back to what it was *)
  1730.                 UseResFile(currResRef);
  1731.                 SetResLoad(TRUE)
  1732.             end
  1733.     end;
  1734.  
  1735.  
  1736. {$S ProcessGuts}
  1737. (*******************************************************************************}
  1738. {* Private: LaunchCycle - Attempt to launch a process}
  1739. {*}
  1740. {* This routine calls the LaunchProcess routine that’s in the UProcessUtils unit.}
  1741. {* The launchFile parameter specifies the file to launch.  The docList parameter}
  1742. {* specifies the list of documents to pass to the launched application for it to}
  1743. {* open or print.  The launchOptions parameter specifies the initial set of}
  1744. {* launch options to use when launching.  The the section titled “Specifying}
  1745. {* Launch Options” in the Process Manager chapter of Inside Macintosh VI for the}
  1746. {* a list and description of the launch options that you can pass in this}
  1747. {* parameter.}
  1748. {*}
  1749. {* If the Process Manager denies the launch, then LaunchProcess returns the}
  1750. {* resulting error code in the LaunchError flag.  If this happens and if the}
  1751. {* error happened to be that the machine is in 32-bit addressing mode and the}
  1752. {* application’s SIZE resource doesn’t have the 32-bit clean flag on, or if there}
  1753. {* isn’t enough memory to launch the application or desk accessory, then an alert}
  1754. {* is presented to the user asking if he or she wants to continue anyway.  If the}
  1755. {* user specifies that he or she does, then launch options are added to the ones}
  1756. {* passed in the launchOptions parameter which allow 32-bit unclean applications}
  1757. {* to launch or to allow the launch into available memory, and then LaunchProcess}
  1758. {* is called again.  This is repeated either until the application or desk}
  1759. {* accessory is successfully launched, the user chose not to launch it, or until}
  1760. {* an unrecoverable error occurs.}
  1761. {*******************************************************************************)
  1762.  
  1763.     procedure LaunchCycle (launchFile: FSSpec; docList: DocListHnd; launchOptions: Integer);
  1764.  
  1765.         var
  1766.             processNum: ProcessSerialNumber; {Serial number of launched process}
  1767.             attemptLaunch: Boolean;             {TRUE if continuing launch attempt}
  1768.             result: Integer;             {Result of caution alert}
  1769.             launchError: OSErr;               {Launch error code}
  1770.             error: OSErr;
  1771.  
  1772.     begin
  1773.         (* Repeat until successful launch or cancelled launch *)
  1774.         repeat
  1775.             (* Attempt to launch the process *)
  1776.             error := LaunchProcess(launchFile, nil, docList, launchOptions, processNum, launchError);
  1777.                     (*<*)
  1778.  (*<*)
  1779.  
  1780.             (* Check for launching errors *)
  1781.             if launchError <> noErr then
  1782.                 begin
  1783.                     (* There was a launching error, present to user *)
  1784.                     if launchError = appModeErr then
  1785.                         begin
  1786.                             (* Ask user if it’s OK to launch 32-bit unclean app *)
  1787.                             result := ShowCautionOKCancelAlert(rMiscWrnMessages, kMiscWrnUncleanMsg);
  1788.                             if result = ok then
  1789.                                 begin
  1790.                                     (* Try launch again, allowing 32-bit unclean app *)
  1791.                                     launchOptions := BOR(launchOptions, launchAllow24Bit);
  1792.                                     attemptLaunch := TRUE
  1793.                                 end
  1794.                             else
  1795.                                 attemptLaunch := FALSE
  1796.                         end
  1797.                     else if launchError = memFullErr then
  1798.                         begin
  1799.                             (* Ask user if it’s OK to launch w/ < requested memory *)
  1800.                             result := ShowCautionOKCancelAlert(rMiscWrnMessages, kMiscWrnLaunchMemMsg);
  1801.                             if result = ok then
  1802.                                 begin
  1803.                                     (* Try launch again, with less than requested mem *)
  1804.                                     launchOptions := BOR(launchOptions, launchUseMinimum);
  1805.                                     attemptLaunch := TRUE
  1806.                                 end
  1807.                             else
  1808.                                 attemptLaunch := FALSE
  1809.                         end
  1810.                     else
  1811.                         begin
  1812.                             (* Some error we don’t handle happened *)
  1813.                             result := ShowStopAlert(rMiscErrMessages, kMiscErrUnknownMsg);
  1814.                             attemptLaunch := FALSE
  1815.                         end
  1816.                 end
  1817.             else if error <> noErr then
  1818.                 begin
  1819.                     result := ShowStopAlert(rMiscErrMessages, kMiscErrUnknownMsg);
  1820.                     attemptLaunch := FALSE
  1821.                 end
  1822.             else
  1823.                 attemptLaunch := FALSE
  1824.         until not attemptLaunch;
  1825.     end;
  1826.  
  1827.  
  1828. {$S ProcessGuts}
  1829. (*******************************************************************************}
  1830. {* Public: DoLaunchInFront}
  1831. {*}
  1832. {* If the user is launching with documents, then only applications are presented}
  1833. {* to the user in the standard-file dialog.  If the user only wants to launch}
  1834. {* without any documents, then both applications and files containing desk}
  1835. {* accessories are presented to the user.}
  1836. {*******************************************************************************)
  1837.  
  1838.     procedure DoLaunchInFront;
  1839.  
  1840.         var
  1841.             reply: StandardFileReply; {Reply from SFGetFile}
  1842.             typeList: SFTypeList;        {List of file types for SF}
  1843.             launchSpec: FSSpec;            {Location of selected app/DA}
  1844.             docList: DocListHnd;        {Handle to the document list}
  1845.             gettingDocs: Boolean;           {True if user still getting docs}
  1846.             launchMode: LaunchModeCode;    {Current launch mode}
  1847.             error: OSErr;
  1848.  
  1849.     begin
  1850.         (* Get the user’s choice for a file to launch *)
  1851.         launchMode := GetLaunchMode;
  1852.         if launchMode = kJustLaunch then
  1853.             (* Just launching, so launch applications and DAs *)
  1854.             StandardGetFile(@AppDAFilter, -1, typeList, reply) (*<*)
  1855.         else if (launchMode = kOpenLaunch) or (launchMode = kPrintLaunch) then
  1856.             begin
  1857.                 (* Launching with documents, so launch applications only *)
  1858.                 typeList[0] := 'APPL';
  1859.                 StandardGetFile(nil, 1, typeList, reply) (*<*)
  1860.             end;
  1861.  
  1862.         if reply.sfGood then
  1863.             begin
  1864.                 launchSpec := reply.sfFile;
  1865.  
  1866.                 (* Check to see if documents should be opened/printed as well *)
  1867.                 if (launchMode = kOpenLaunch) or (launchMode = kPrintLaunch) then
  1868.                     begin
  1869.                         (* Create an empty list of documents *)
  1870.                         docList := CreateDocList(launchMode);
  1871.  
  1872.                         (* Keep getting documents until user chooses Cancel *)
  1873.                         gettingDocs := TRUE;
  1874.                         while gettingDocs do
  1875.                             begin
  1876.                                 StandardGetFile(nil, -1, typeList, reply); (*<*)
  1877.                                 if reply.sfGood then
  1878.                                     error := AddToDocList(reply.sfFile, docList) (*◊*)
  1879.                                 else
  1880.                                     gettingDocs := FALSE
  1881.                             end
  1882.                     end
  1883.                 else
  1884.                     docList := nil;
  1885.  
  1886.                 (* Attempt to launch the application *)
  1887.                 LaunchCycle(launchSpec, docList, launchContinue);
  1888.  
  1889.                 (* Dispose of the document list, if there was one *)
  1890.                 if docList <> nil then
  1891.                     DisposeDocList(docList)
  1892.             end
  1893.     end;
  1894.  
  1895.  
  1896. {$S ProcessGuts}
  1897. (*******************************************************************************}
  1898. {* Public: DoLaunchInBack}
  1899. {*}
  1900. {* For the moment, I’m using SFGetFile to choose files rather than}
  1901. {* StandardGetFile because StandardGetFile has a bug in that file filtering}
  1902. {* doesn’t work right.  This is supposed to be fixed in b2, so I’ll change this}
  1903. {* call once that version is released.  This will make the call to FSMakeFSSpec}
  1904. {* unnecessary because StandardGetFile returns the FSSpec of the chosen file.}
  1905. {*******************************************************************************)
  1906.  
  1907.     procedure DoLaunchInBack;
  1908.  
  1909.         var
  1910.             reply: StandardFileReply; {Reply from SFGetFile}
  1911.             typeList: SFTypeList;        {List of file types to diplay in SF}
  1912.             launchSpec: FSSpec;            {Location of selected application}
  1913.             docList: DocListHnd;        {Handle to the document list}
  1914.             gettingDocs: Boolean;           {True if user still getting docs}
  1915.             launchMode: LaunchModeCode;    {Current launch mode}
  1916.             error: OSErr;
  1917.  
  1918.     begin
  1919.         (* Get the user’s choice for an application to launch *)
  1920.         typeList[0] := 'APPL';
  1921.         StandardGetFile(nil, 1, typeList, reply); (*<*)
  1922.  
  1923.         if reply.sfGood then
  1924.             begin
  1925.                 (* Convert working directory and file name to FSSpec *)
  1926.                 launchSpec := reply.sfFile;
  1927.  
  1928.                 launchMode := GetLaunchMode;
  1929.                 if (launchMode = kOpenLaunch) or (launchMode = kPrintLaunch) then
  1930.                     begin
  1931.                         (* Create an empty list of documents *)
  1932.                         docList := CreateDocList(launchMode);
  1933.  
  1934.                         (* Keep getting documents until user chooses Cancel *)
  1935.                         gettingDocs := TRUE;
  1936.                         while gettingDocs do
  1937.                             begin
  1938.                                 StandardGetFile(nil, -1, typeList, reply); (*<*)
  1939.                                 if reply.sfGood then
  1940.                                     error := AddToDocList(reply.sfFile, docList) (*◊*)
  1941.                                 else
  1942.                                     gettingDocs := FALSE
  1943.                             end
  1944.                     end
  1945.                 else
  1946.                     docList := nil;
  1947.  
  1948.                 (* Attempt to launch the application *)
  1949.                 LaunchCycle(launchSpec, docList, launchContinue + launchDontSwitch);
  1950.  
  1951.                 (* Dispose of the document list, if there was one *)
  1952.                 if docList <> nil then
  1953.                     DisposeDocList(docList)
  1954.             end
  1955.     end;
  1956.  
  1957.  
  1958. {$S ProcessGuts}
  1959. (*******************************************************************************}
  1960. {* Public: DoLaunchTo}
  1961. {*}
  1962. {* For the moment, I’m using SFGetFile to choose files rather than}
  1963. {* StandardGetFile because StandardGetFile has a bug in that file filtering}
  1964. {* doesn’t work right.  This is supposed to be fixed in b2, so I’ll change this}
  1965. {* call once that version is released.  This will make the call to FSMakeFSSpec}
  1966. {* unnecessary because StandardGetFile returns the FSSpec of the chosen file.}
  1967. {*******************************************************************************)
  1968.  
  1969.     procedure DoLaunchTo;
  1970.  
  1971.         var
  1972.             reply: StandardFileReply; {Reply from SFGetFile}
  1973.             typeList: SFTypeList;        {List of file types to diplay in SF}
  1974.             launchSpec: FSSpec;            {Location of selected file}
  1975.             docList: DocListHnd;        {Handle to the document list}
  1976.             gettingDocs: Boolean;           {True if user still getting docs}
  1977.             launchMode: LaunchModeCode;    {Current launch mode}
  1978.             error: OSErr;
  1979.  
  1980.     begin
  1981.         (* Get the user’s choice for a file to launch *)
  1982.         launchMode := GetLaunchMode;
  1983.         if launchMode = kJustLaunch then
  1984.             (* Just launching, so launch applications and DAs *)
  1985.             StandardGetFile(@AppDAFilter, -1, typeList, reply) (*<*)
  1986.         else if (launchMode = kOpenLaunch) or (launchMode = kPrintLaunch) then
  1987.             begin
  1988.                 (* Launching with documents, so launch applications only *)
  1989.                 typeList[0] := 'APPL';
  1990.                 StandardGetFile(nil, 1, typeList, reply) (*<*)
  1991.             end;
  1992.  
  1993.         if reply.sfGood then
  1994.             begin
  1995.                 (* Convert working directory and file name to FSSpec *)
  1996.                 launchSpec := reply.sfFile;
  1997.  
  1998.                 if (launchMode = kOpenLaunch) or (launchMode = kPrintLaunch) then
  1999.                     begin
  2000.                         (* Create an empty list of documents *)
  2001.                         docList := CreateDocList(launchMode);
  2002.  
  2003.                         (* Keep getting documents until user chooses Cancel *)
  2004.                         gettingDocs := TRUE;
  2005.                         while gettingDocs do
  2006.                             begin
  2007.                                 StandardGetFile(nil, -1, typeList, reply); (*<*)
  2008.                                 if reply.sfGood then
  2009.                                     error := AddToDocList(reply.sfFile, docList) (*◊*)
  2010.                                 else
  2011.                                     gettingDocs := FALSE
  2012.                             end
  2013.                     end
  2014.                 else
  2015.                     docList := nil;
  2016.  
  2017.                 (* Attempt to launch the application or DA *)
  2018.                 LaunchCycle(launchSpec, docList, launchAllow24Bit);
  2019.  
  2020.                 (* Dispose of the document list, if there was one *)
  2021.                 if docList <> nil then
  2022.                     DisposeDocList(docList)
  2023.             end
  2024.     end;
  2025.  
  2026.  
  2027. {$S ProcessGuts}
  2028. (*******************************************************************************}
  2029. {* Public: DoLaunchMode}
  2030. {*}
  2031. {* SetLaunchMode does most of the work, and there isn’t much to do.}
  2032. {*******************************************************************************)
  2033.  
  2034.     procedure DoLaunchMode (modeItem: Integer);
  2035.  
  2036.     begin
  2037.         case modeItem of
  2038.             iJustLaunch: 
  2039.                 SetLaunchMode(kJustLaunch);
  2040.             iOpenLaunch: 
  2041.                 SetLaunchMode(kOpenLaunch);
  2042.             iPrintLaunch: 
  2043.                 SetLaunchMode(kPrintLaunch)
  2044.         end
  2045.     end;
  2046.  
  2047.  
  2048. {$S ProcessGuts}
  2049. (*******************************************************************************}
  2050. {* Public: DoBringProcessToFront}
  2051. {*}
  2052. {* The List Manager is called to get each selection in the process list window.}
  2053. {* SetFrontProcess is called with the process serial number of each selected}
  2054. {* process.  They aren’t immediately brought to the front when SetFrontProcess is}
  2055. {* called.  Instead, they are scheduled to come to the front in the same order as}
  2056. {* they were presented to SetFrontProcess.  Once ProcDoggie reenters the main}
  2057. {* event loop, the Process Manager brings each scheduled process to the front in}
  2058. {* turn.}
  2059. {*}
  2060. {* At the moment, I can’t get ProcDoggie itself to be scheduled.  I assume it’s}
  2061. {* because SetFrontProcess checks to see if process serial number you passed it}
  2062. {* is the same as the process serial number of the current process.  If it is, it}
  2063. {* doesn’t bother to schedule the process.  I’m not quite sure how to work around}
  2064. {* that.}
  2065. {*******************************************************************************)
  2066.  
  2067.     procedure DoBringProcessToFront (processListWindow: WindowPtr);
  2068.  
  2069.         const
  2070.             kFindNext = TRUE; {Pass to LGetSelect to find sequence of selections}
  2071.  
  2072.         var
  2073.             procList: ListHandle;         {Handle to List Mgr process list}
  2074.             currCell: Point;              {Cell that has selection}
  2075.             listInfo: ProcessListInfoRec; {Process info from List Mgr list}
  2076.             gotSelection: Boolean;            {T if got sel’d cell, F if no more}
  2077.             listInfoLen: Integer;            {Length of list info in bytes}
  2078.             error: OSErr;
  2079.  
  2080.     begin
  2081.         (* Get the List Manager’s copy of the process list *)
  2082.         procList := ListHandle(GetWRefCon(processListWindow));
  2083.  
  2084.         (* Keep looping until all selected processes have been brought to front *)
  2085.         currCell.v := 0;
  2086.         currCell.h := 0;
  2087.         gotSelection := TRUE;
  2088.         while gotSelection do
  2089.             begin
  2090.                 gotSelection := LGetSelect(kFindNext, currCell, procList); (*◊*)
  2091.                 if gotSelection then
  2092.                     begin
  2093.                         listInfoLen := SIZEOF(ProcessListInfoRec);
  2094.                         LGetCell(Ptr(@listInfo), listInfoLen, currCell, procList); (*◊*)
  2095.                         error := SetFrontProcess(listInfo.serialNumber);
  2096.                         currCell.v := currCell.v + 1
  2097.                     end
  2098.             end
  2099.     end;
  2100.  
  2101.  
  2102. {$S ProcessGuts}
  2103. (*******************************************************************************}
  2104. {* Public: DoGetProcessInfo}
  2105. {*}
  2106. {* This routine loops until Process Information windows for all selected}
  2107. {* processes in the Process List window are displayed.  Information for each}
  2108. {* process in the process list is retrieved from the list itself.  Then, that}
  2109. {* process is compared against all existing Process Information windows.  If a}
  2110. {* Process Information window already exists for that process, then that window}
  2111. {* is simply activated and DoGetProcessInfo exits.  Otherwise, the Process}
  2112. {* Manager is called to retrieve information for that process.  A new Process}
  2113. {* Information window is created, and its contents are set to the information}
  2114. {* retrieved for the process.}
  2115. {*******************************************************************************)
  2116.  
  2117.     procedure DoGetProcessInfo (processListWindow: WindowPtr);
  2118.  
  2119.         const
  2120.             kFindNext = TRUE; {Pass to LGetSelect to find sequence of selections}
  2121.  
  2122.         var
  2123.             procList: ListHandle;         {Handle to List Mgr proc list}
  2124.             currCell: Point;              {Cell that has selection}
  2125.             listInfo: ProcessListInfoRec; {Proc info from List Mgr list}
  2126.             gotSelection: Boolean;            {T if got sel’d cell, F if none}
  2127.             listInfoLen: Integer;            {Length of list info in bytes}
  2128.             processInfo: ProcessInfoRec;     {Info about selected processes}
  2129.             procName: Str31;              {Name of selected processes}
  2130.             procSpec: FSSpec;             {File spec of sel’d processes}
  2131.             processInfoWindow: WindowPtr;          {Ptr to new process info window}
  2132.             psnHandle: Handle;             {Handle to PSN of chosen proc}
  2133.             existingWindow: WindowPtr;          {Proc info wind if already open}
  2134.             error: OSErr;
  2135.  
  2136.         procedure HandleError (messageClass: Integer; messageIndex: Integer);
  2137.  
  2138.             var
  2139.                 result: Integer; {Result of alert; ignored}
  2140.  
  2141.         begin
  2142.             if processInfoWindow <> nil then
  2143.                 CloseProcessInfoWindow(processInfoWindow);
  2144.             result := ShowStopAlert(messageClass, messageIndex);
  2145.             gError := noErr;
  2146.             EXIT(DoGetProcessInfo)
  2147.         end;
  2148.  
  2149.     begin
  2150.         (* Get the List Manager’s copy of the process list *)
  2151.         procList := ListHandle(GetWRefCon(processListWindow));
  2152.  
  2153.         (* Keep looping until all selected processes have been brought to front *)
  2154.         currCell.v := 0;
  2155.         currCell.h := 0;
  2156.         gotSelection := TRUE;
  2157.         while gotSelection do
  2158.             begin
  2159.                 gotSelection := LGetSelect(kFindNext, currCell, procList); (*◊*)
  2160.                 if gotSelection then
  2161.                     begin
  2162.                         listInfoLen := SIZEOF(ProcessListInfoRec);
  2163.                         LGetCell(Ptr(@listInfo), listInfoLen, currCell, procList); (*◊*)
  2164.  
  2165.                         (* See if proc info wind already exists for selected proc *)
  2166.                         existingWindow := FindProcessInfoWindow(listInfo.serialNumber);
  2167.                         if existingWindow <> nil then
  2168.                             SelectWindow(existingWindow)
  2169.                         else
  2170.                             begin
  2171.                                 (* Get information about an open process *)
  2172.                                 processInfo.processInfoLength := SIZEOF(ProcessInfoRec);
  2173.                                 processInfo.processName := @procName;
  2174.                                 processInfo.processAppSpec := @procSpec;
  2175.                                 error := GetProcessInformation(listInfo.serialNumber, processInfo);
  2176.                                         (*◊*)
  2177.                                 if error <> noErr then
  2178.                                     HandleError(rMiscErrMessages, kMiscErrUnknownMsg);
  2179.  
  2180.                                 (* Create the process information window *)
  2181.                                 processInfoWindow := CreateProcessInfoWindow;
  2182.                                 if processInfoWindow <> nil then
  2183.                                     begin
  2184.                                         (* Put handle to PSN into refCon *)
  2185.                                         psnHandle := NewHandleMargin(SIZEOF(ProcessSerialNumber), kAllocApp, not kAllocClr);
  2186.                                         if psnHandle = nil then
  2187.                                             HandleError(rMemErrMessages, kMemErrProcInfoOpenMsg);
  2188.                                         BlockMove(Ptr(@processInfo.processNumber), psnHandle^, SIZEOF(ProcessSerialNumber));
  2189.                                         SetWRefCon(processInfoWindow, LongInt(psnHandle));
  2190.  
  2191.                                         (* Update dlog items to reflect proc info *)
  2192.                                         SetUpProcessInfoItems(processInfoWindow, processInfo);
  2193.                                     end
  2194.                                 else
  2195.                                     gotSelection := FALSE
  2196.                             end;
  2197.  
  2198.                         (* Go to the next cell *)
  2199.                         currCell.v := currCell.v + 1
  2200.                     end
  2201.             end
  2202.     end;
  2203.  
  2204.  
  2205. {$S ProcessGuts}
  2206. (*******************************************************************************}
  2207. {* Public: DoTerminateProcess}
  2208. {*}
  2209. {* The List Manager is used to get all of the selected processes in}
  2210. {* processListWindow.  The process serial number of each of these processes is}
  2211. {* extracted and is then used when calling TerminateProcess.}
  2212. {*******************************************************************************)
  2213.  
  2214.     procedure DoTerminateProcess (processListWindow: WindowPtr);
  2215.  
  2216.         const
  2217.             kFindNext = TRUE; {Pass to LGetSelect to find sequence of selections}
  2218.  
  2219.         var
  2220.             procList: ListHandle;         {Handle to List Mgr process list}
  2221.             currCell: Point;              {Cell that has selection}
  2222.             listInfo: ProcessListInfoRec; {Process info from List Mgr list}
  2223.             listInfoLen: Integer;            {Length of list info in bytes}
  2224.             gotSelection: Boolean;            {T if got sel’d cell, F if none}
  2225.             error: OSErr;
  2226.  
  2227.         procedure HandleError (messageClass: Integer; messageIndex: Integer);
  2228.  
  2229.             var
  2230.                 result: Integer; {Result of alert; ignored}
  2231.  
  2232.         begin
  2233.             result := ShowStopAlert(messageClass, messageIndex);
  2234.             gError := noErr;
  2235.             EXIT(DoTerminateProcess)
  2236.         end;
  2237.  
  2238.     begin
  2239.         (* Get the List Manager’s copy of the process list *)
  2240.         procList := ListHandle(GetWRefCon(processListWindow));
  2241.  
  2242.         (* Keep looping until all selected processes have been terminated *)
  2243.         currCell.v := 0;
  2244.         currCell.h := 0;
  2245.         gotSelection := TRUE;
  2246.         while gotSelection do
  2247.             begin
  2248.                 gotSelection := LGetSelect(kFindNext, currCell, procList); (*◊*)
  2249.                 if gotSelection then
  2250.                     begin
  2251.                         listInfoLen := SIZEOF(ProcessListInfoRec);
  2252.                         LGetCell(Ptr(@listInfo), listInfoLen, currCell, procList); (*◊*)
  2253.  
  2254.                         (* Kill the specified process *)
  2255.                         error := TerminateProcess(listInfo.serialNumber);
  2256.                         if error <> noErr then
  2257.                             HandleError(rMiscErrMessages, kMiscErrUnknownMsg);
  2258.  
  2259.                         (* Go to the next cell *)
  2260.                         currCell.v := currCell.v + 1
  2261.                     end
  2262.             end
  2263.     end;
  2264.  
  2265. {$S %A5Init}
  2266. (*******************************************************************************}
  2267. {* Public: DoAppleMenu}
  2268. {*}
  2269. {* The menu guide array is initialized with the menu handles and enable flags of}
  2270. {* all menus.}
  2271. {*}
  2272. {* If GetNewMBar couldn’t load the MBAR resource, then it returns NIL and the}
  2273. {* error code is in ResError, and I can deal with the error elegantly.  But, if}
  2274. {* the GetNewMBar couldn’t load the menus themselves, then it’ll probably crash.}
  2275. {*******************************************************************************)
  2276.  
  2277.     procedure StartMenus;
  2278.  
  2279.         var
  2280.             menuBar: Handle;  {Handle to the menu bar from the MBAR resource}
  2281.             menuIndex: Integer; {Index into menu guide records}
  2282.  
  2283.     begin
  2284.         (* Load in the menu bar *)
  2285.         menuBar := GetNewMBar(rMenuBar);
  2286.         if menuBar <> nil then
  2287.             begin
  2288.                 (* Set it, then dispose of it because SetMenuBar makes a copy *)
  2289.                 SetMenuBar(menuBar);
  2290.                 DisposHandle(menuBar);
  2291.  
  2292.                 (* Add the desk accessories to the Apple menu *)
  2293.                 AddResMenu(GetMHandle(mApple), 'DRVR');
  2294.  
  2295.                 (* Initialize the menu guide array *)
  2296.                 for menuIndex := mFile to mProcess do
  2297.                     begin
  2298.                         gMenuGuides[menuIndex].theMenu := GetMHandle(menuIndex);
  2299.                         gMenuGuides[menuIndex].enables := gMenuGuides[menuIndex].theMenu^^.enableFlags
  2300.                     end;
  2301.  
  2302.                 (* Draw the menu bar *)
  2303.                 DrawMenuBar
  2304.             end
  2305.         else if ResError = memFullErr then
  2306.             gError := memFullErr
  2307.         else if (ResError = noErr) or (ResError = resNotFound) then
  2308.             gError := resNotFound
  2309.         else
  2310.             gError := dsSysErr
  2311.     end;
  2312.  
  2313.  
  2314. {$S Main}
  2315. (*******************************************************************************}
  2316. {* Private: DoAppleMenu - Handle an Apple menu item choice}
  2317. {*}
  2318. {* This routine is called whenever it’s determined that the chosen menu item was}
  2319. {* in the Apple menu.  If the chosen menu item that’s passed in the menuItem}
  2320. {* parameter wasn’t the About item, the name of the menu item is retrieved and}
  2321. {* then OpenDeskAcc is called with this name so that the desk accessory by that}
  2322. {* name is opened.  The Process Manager can launch desk accessories, but}
  2323. {* OpenDeskAcc should still be used if the user chooses any item in the Apple}
  2324. {* menu.}
  2325. {*******************************************************************************)
  2326.  
  2327.     procedure DoAppleMenu (menuItem: Integer);
  2328.  
  2329.         var
  2330.             daName: Str255;  {Name of the chosen DA}
  2331.             refNum: Integer; {Reference number of the DA, ignored}
  2332.  
  2333.     begin
  2334.         if menuItem = iAbout then
  2335.             ShowAboutBox
  2336.         else
  2337.             begin
  2338.                 GetItem(GetMHandle(mApple), menuItem, daName); (*<*)
  2339.                 refNum := OpenDeskAcc(daName)
  2340.             end
  2341.     end;
  2342.  
  2343.  
  2344. {$S Main}
  2345. (*******************************************************************************}
  2346. {* Private: DoFileMenu - Handle a File menu item choice}
  2347. {*}
  2348. {* This routine is called whenever it’s determined that the chosen menu item was}
  2349. {* in the File menu.  The item number of the chosen menu item is passed in the}
  2350. {* menuItem parameter.}
  2351. {*******************************************************************************)
  2352.  
  2353.     procedure DoFileMenu (menuItem: Integer);
  2354.  
  2355.     begin
  2356.         case menuItem of
  2357.             iLaunchFore: 
  2358.                 DoLaunchInFront;
  2359.             iLaunchBack: 
  2360.                 DoLaunchInBack;
  2361.             iLaunchTo: 
  2362.                 DoLaunchTo;
  2363.             iJustLaunch, iOpenLaunch, iPrintLaunch: 
  2364.                 DoLaunchMode(menuItem);
  2365.             iQuit: 
  2366.                 DoQuit
  2367.         end
  2368.     end;
  2369.  
  2370.  
  2371. {$S Main}
  2372. (*******************************************************************************}
  2373. {* Private: DoProcessMenu - Handle a Process menu item choice}
  2374. {*}
  2375. {* This routine is called whenever it’s determined that the chosen menu item was}
  2376. {* in the Process menu.  The item number of the chosen menu item is passed in the}
  2377. {* menuItem parameter.}
  2378. {*******************************************************************************)
  2379.  
  2380.     procedure DoProcessMenu (menuItem: Integer);
  2381.  
  2382.     begin
  2383.         case menuItem of
  2384.             iBringFront: 
  2385.                 DoBringProcessToFront(FrontWindow);
  2386.             iShowProcessInfo: 
  2387.                 DoGetProcessInfo(FrontWindow);
  2388.             iTerminateProcess: 
  2389.                 DoTerminateProcess(FrontWindow)
  2390.         end
  2391.     end;
  2392.  
  2393.  
  2394. {$S Main}
  2395. (*******************************************************************************}
  2396. {* Public: DoMenuChoice}
  2397. {*}
  2398. {* This routine should be self-explanatory.}
  2399. {*******************************************************************************)
  2400.  
  2401.     procedure DoMenuChoice (menuChoice: LongInt);
  2402.  
  2403.         var
  2404.             menuNum: Integer; {Menu number of chosen menu}
  2405.             menuItem: Integer; {Item number of chosen menu item}
  2406.  
  2407.     begin
  2408.         if menuChoice <> 0 then
  2409.             begin
  2410.                 (* Get the chosen menu item and menu number *)
  2411.                 menuNum := HiWord(menuChoice);
  2412.                 menuItem := LoWord(menuChoice);
  2413.  
  2414.                 (* Dispatch the appropriate menu-handling routine *)
  2415.                 case menuNum of
  2416.                     mApple: 
  2417.                         DoAppleMenu(menuItem);
  2418.                     mFile: 
  2419.                         DoFileMenu(menuItem);
  2420.                     mProcess: 
  2421.                         DoProcessMenu(menuItem);
  2422.                 end;
  2423.                 HiliteMenu(0)
  2424.             end
  2425.     end;
  2426.  
  2427.  
  2428. {$S Main}
  2429. (*******************************************************************************}
  2430. {* Private: ResetMenuItems - Disable any disableable items and clear marks}
  2431. {*}
  2432. {* Disabling all the menu items is done bruteforcedly.  It could easily be done}
  2433. {* by looping through each menu and disabling every item that comes up (disabling}
  2434. {* the Font menu is done this way), but I thought doing it using the brute-force}
  2435. {* method was clearer.  Then again. . .}
  2436. {*******************************************************************************)
  2437.  
  2438.     procedure ResetMenuItems;
  2439.  
  2440.         var
  2441.             aMenu: MenuHandle; {Handle to each menu we’re disabling}
  2442.  
  2443.     begin
  2444.         (* Disable items in the File menu *)
  2445.         aMenu := GetMHandle(mFile);
  2446.         DisableItem(aMenu, iLaunchFore);
  2447.         DisableItem(aMenu, iLaunchBack);
  2448.         DisableItem(aMenu, iLaunchTo);
  2449.         SetItemMark(aMenu, iJustLaunch, CHR(noMark));
  2450.         SetItemMark(aMenu, iOpenLaunch, CHR(noMark));
  2451.         SetItemMark(aMenu, iPrintLaunch, CHR(noMark));
  2452.  
  2453.         (* Disable items in the Process menu *)
  2454.         aMenu := GetMHandle(mProcess);
  2455.         DisableItem(aMenu, iBringFront);
  2456.         DisableItem(aMenu, iShowProcessInfo);
  2457.         DisableItem(aMenu, iTerminateProcess);
  2458.     end;
  2459.  
  2460.  
  2461. {$S Main}
  2462. (*******************************************************************************}
  2463. {* Public: FixMenus}
  2464. {*}
  2465. {* FixMenus first disables every available menu item.  Then the most basic menu}
  2466. {* items are enabled.  The windowKind field of the front window is then checked.}
  2467. {* If there is a window open, FixMenus calls a routine that’s responsible for}
  2468. {* that kind of window to enable any menu items that are relevant to that kind of}
  2469. {* window.}
  2470. {*}
  2471. {* If the front window is a modal dialog, then the basic set of menu items are}
  2472. {* NOT enabled, and the entire Apple menu is disabled.}
  2473. {*}
  2474. {* After this is done, the menu bar might have to be redrawn to reflect the new}
  2475. {* conditions.  So, FixMenus go through every menu to determine if the state of}
  2476. {* the entire menu has changed.  The MenuGuide records are used to help determine}
  2477. {* this.  If the state of any many has changed, then the menu bar is redrawn.}
  2478. {*******************************************************************************)
  2479.  
  2480.     procedure FixMenus;
  2481.  
  2482.         var
  2483.             currWindow: WindowPtr;  {Pointer to the front-most window}
  2484.             currMenu: MenuHandle; {Handle to menu being enabled}
  2485.             oldEnables: LongInt;    {True if 1+ menu items enabled when FixMenus called}
  2486.             newEnables: LongInt;    {True if 1+ menu items enabled after menus fixed}
  2487.             mustRedraw: Boolean;    {TRUE if menu bar has to be redrawn}
  2488.             numItems: Integer;    {Number of items in a menu}
  2489.             menuIndex: Integer;    {Index into menu guide array}
  2490.  
  2491.     begin
  2492.         (* Start by disabling all menus *)
  2493.         ResetMenuItems;
  2494.  
  2495.         (* Front-most window determines most menu enabling/disabling *)
  2496.         currWindow := FrontWindow;
  2497.  
  2498.         (* Fix the marks for the launch mode items *)
  2499.         currMenu := GetMHandle(mFile);
  2500.         case GetLaunchMode of
  2501.             kJustLaunch: 
  2502.                 CheckItem(currMenu, iJustLaunch, TRUE);
  2503.             kOpenLaunch: 
  2504.                 CheckItem(currMenu, iOpenLaunch, TRUE);
  2505.             kPrintLaunch: 
  2506.                 CheckItem(currMenu, iPrintLaunch, TRUE)
  2507.         end;
  2508.  
  2509.         (* Enable any window-specific menu items *)
  2510.         if currWindow <> nil then
  2511.             if IsProcessListWindow(currWindow) then
  2512.                 (* Process list window in front, set up menu items in it *)
  2513.                 FixProcessListMenus(currWindow)
  2514.             else if IsProcessInfoWindow(currWindow) then
  2515.                 (* Process info window in front, set up menu items in it *)
  2516.                 FixProcessInfoMenus(currWindow);
  2517.  
  2518.         (* Assume we don’t have to redraw the menu bar *)
  2519.         mustRedraw := FALSE;
  2520.  
  2521.         (* Check through every menu to see if there are any enabled items in it *)
  2522.         for menuIndex := mFirst to mLast do
  2523.             begin
  2524.                 (* Grab the old and new enable flags excluding the flag for the entire menu *)
  2525.                 oldEnables := BAnd(gMenuGuides[menuIndex].enables, $FFFFFFFE);
  2526.                 newEnables := BAnd(gMenuGuides[menuIndex].theMenu^^.enableFlags, $FFFFFFFE);
  2527.  
  2528.                 (* Shift left so that we only see flags for existing items *)
  2529.                 numItems := CountMItems(gMenuGuides[menuIndex].theMenu);
  2530.                 oldEnables := BitShift(oldEnables, 31 - numItems);
  2531.                 newEnables := BitShift(newEnables, 31 - numItems);
  2532.  
  2533.                 (* Determine if the menu bar must be redrawn *)
  2534.                 if (oldEnables <> 0) and (newEnables = 0) then
  2535.                     begin
  2536.                         (* Had some items enabled, now has no items enabled, redraw *)
  2537.                         DisableItem(gMenuGuides[menuIndex].theMenu, 0);
  2538.                         mustRedraw := TRUE
  2539.                     end
  2540.                 else if (oldEnables = 0) and (newEnables <> 0) then
  2541.                     begin
  2542.                         (* Had no items enabled, now has some items enabled, redraw *)
  2543.                         EnableItem(gMenuGuides[menuIndex].theMenu, 0);
  2544.                         mustRedraw := TRUE
  2545.                     end;
  2546.  
  2547.                 (* Update our copy of the enable flags *)
  2548.                 gMenuGuides[menuIndex].enables := gMenuGuides[menuIndex].theMenu^^.enableFlags
  2549.             end;
  2550.  
  2551.         (* If at least one menu has changed state, must redraw the menu bar *)
  2552.         if mustRedraw then
  2553.             InvalMenuBar
  2554.     end;
  2555.  
  2556. end.